Don't resolve already resolved paths when parsing ld.config.txt
When parsing ld.config.txt, paths in *.search.paths properties are
resolved using resolved_paths(). This is causing a number of newfstatat
system calls during a process start-up and is contributing about 3.6%
of the entire linker initialization time.
Optimize the overhead by not resolving already resolved paths; resolved
paths are cached.
Bug: 132348336
Test: strace -C -e newfstatat ls shows the number of the syscalls is
reduced from 131 to 99
Test: simpleperf record --call-graph fp -f 10000000 -o /data/perf.data ls
simpleperf report -g -i /data/perf.data
shows percentage of get_paths() from 2.9% to 2.5%
Change-Id: I79823255a89f6e2a63b80c74e6768850276ab536
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 7741904..46c91a3 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -417,9 +417,22 @@
if (resolve) {
std::vector<std::string> resolved_paths;
-
- // do not remove paths that do not exist
- resolve_paths(paths, &resolved_paths);
+ for (const auto& path : paths) {
+ if (path.empty()) {
+ continue;
+ }
+ // this is single threaded. no need to lock
+ auto cached = resolved_paths_.find(path);
+ if (cached == resolved_paths_.end()) {
+ resolved_paths_[path] = resolve_path(path);
+ cached = resolved_paths_.find(path);
+ }
+ CHECK(cached != resolved_paths_.end());
+ if (cached->second.empty()) {
+ continue;
+ }
+ resolved_paths.push_back(cached->second);
+ }
return resolved_paths;
} else {
@@ -442,6 +455,7 @@
return it;
}
std::unordered_map<std::string, PropertyValue> properties_;
+ std::unordered_map<std::string, std::string> resolved_paths_;
int target_sdk_version_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Properties);
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index e926671..29110ed 100644
--- a/linker/linker_utils.cpp
+++ b/linker/linker_utils.cpp
@@ -204,47 +204,54 @@
if (path.empty()) {
continue;
}
+ std::string resolved = resolve_path(path);
+ if (!resolved.empty()) {
+ resolved_paths->push_back(std::move(resolved));
+ }
+ }
+}
- char resolved_path[PATH_MAX];
- const char* original_path = path.c_str();
- if (realpath(original_path, resolved_path) != nullptr) {
- struct stat s;
- if (stat(resolved_path, &s) == -1) {
- DL_WARN("Warning: cannot stat file \"%s\": %s (ignoring)", resolved_path, strerror(errno));
- continue;
+std::string resolve_path(const std::string& path) {
+ char resolved_path[PATH_MAX];
+ const char* original_path = path.c_str();
+ if (realpath(original_path, resolved_path) != nullptr) {
+ struct stat s;
+ if (stat(resolved_path, &s) == -1) {
+ DL_WARN("Warning: cannot stat file \"%s\": %s (ignoring)", resolved_path, strerror(errno));
+ return "";
+ }
+ if (!S_ISDIR(s.st_mode)) {
+ DL_WARN("Warning: \"%s\" is not a directory (ignoring)", resolved_path);
+ return "";
+ }
+ return resolved_path;
+ } else {
+ std::string normalized_path;
+ if (!normalize_path(original_path, &normalized_path)) {
+ DL_WARN("Warning: unable to normalize \"%s\" (ignoring)", original_path);
+ return "";
+ }
+
+ std::string zip_path;
+ std::string entry_path;
+ if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) {
+ if (realpath(zip_path.c_str(), resolved_path) == nullptr) {
+ DL_WARN("Warning: unable to resolve \"%s\": %s (ignoring)",
+ zip_path.c_str(), strerror(errno));
+ return "";
}
- if (!S_ISDIR(s.st_mode)) {
- DL_WARN("Warning: \"%s\" is not a directory (ignoring)", resolved_path);
- continue;
- }
- resolved_paths->push_back(resolved_path);
+
+ return std::string(resolved_path) + kZipFileSeparator + entry_path;
} else {
- std::string normalized_path;
- if (!normalize_path(original_path, &normalized_path)) {
- DL_WARN("Warning: unable to normalize \"%s\" (ignoring)", original_path);
- continue;
- }
-
- std::string zip_path;
- std::string entry_path;
- if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) {
- if (realpath(zip_path.c_str(), resolved_path) == nullptr) {
- DL_WARN("Warning: unable to resolve \"%s\": %s (ignoring)",
- zip_path.c_str(), strerror(errno));
- continue;
- }
-
- resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path);
- } else {
- struct stat s;
- if (stat(normalized_path.c_str(), &s) == 0 && S_ISDIR(s.st_mode)) {
- // Path is not a zip path, but an existing directory. Then add it
- // although we failed to resolve it. b/119656753
- resolved_paths->push_back(normalized_path);
- }
+ struct stat s;
+ if (stat(normalized_path.c_str(), &s) == 0 && S_ISDIR(s.st_mode)) {
+ // Path is not a zip path, but an existing directory. Then add it
+ // although we failed to resolve it. b/119656753
+ return normalized_path;
}
}
}
+ return "";
}
bool is_first_stage_init() {
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index 34a597b..5073b10 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -47,6 +47,9 @@
// normalizes entry name by calling normalize_path function.
void resolve_paths(std::vector<std::string>& paths,
std::vector<std::string>* resolved_paths);
+// Resolve a single path. Return empty string when the path is invalid or can't
+// be resolved.
+std::string resolve_path(const std::string& path);
void split_path(const char* path, const char* delimiters, std::vector<std::string>* paths);