Skip to content

Commit df3e89b

Browse files
cferris1000Gerrit Code Review
authored and
Gerrit Code Review
committed
Merge "Speed up map creation."
2 parents 06e0c70 + 60521c7 commit df3e89b

File tree

5 files changed

+590
-60
lines changed

5 files changed

+590
-60
lines changed

libbacktrace/Android.bp

+14
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,17 @@ cc_test {
244244
},
245245
},
246246
}
247+
248+
cc_benchmark {
249+
name: "backtrace_benchmarks",
250+
defaults: ["libbacktrace_common"],
251+
252+
srcs: [
253+
"backtrace_benchmarks.cpp",
254+
],
255+
256+
shared_libs: [
257+
"libbacktrace",
258+
"libbase",
259+
],
260+
}

libbacktrace/backtrace_benchmarks.cpp

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (C) 2017 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <errno.h>
18+
#include <fcntl.h>
19+
#include <stdio.h>
20+
#include <string.h>
21+
#include <sys/prctl.h>
22+
#include <sys/stat.h>
23+
#include <sys/types.h>
24+
#include <sys/wait.h>
25+
#include <unistd.h>
26+
27+
#include <string>
28+
29+
#include <android-base/file.h>
30+
31+
#include <benchmark/benchmark.h>
32+
33+
#include <backtrace/Backtrace.h>
34+
#include <backtrace/BacktraceMap.h>
35+
36+
// Definitions of prctl arguments to set a vma name in Android kernels.
37+
#define ANDROID_PR_SET_VMA 0x53564d41
38+
#define ANDROID_PR_SET_VMA_ANON_NAME 0
39+
40+
constexpr size_t kNumMaps = 2000;
41+
constexpr size_t kNumIterations = 1000;
42+
43+
static bool CountMaps(pid_t pid, size_t* num_maps) {
44+
// Minimize the calls that might allocate memory. If too much memory
45+
// gets allocated, then this routine will add extra maps and the next
46+
// call will fail to get the same number of maps as before.
47+
int fd =
48+
open((std::string("/proc/") + std::to_string(pid) + "/maps").c_str(), O_RDONLY | O_CLOEXEC);
49+
if (fd == -1) {
50+
fprintf(stderr, "Cannot open map file for pid %d: %s\n", pid, strerror(errno));
51+
return false;
52+
}
53+
*num_maps = 0;
54+
while (true) {
55+
char buffer[2048];
56+
ssize_t bytes = read(fd, buffer, sizeof(buffer));
57+
if (bytes <= 0) {
58+
break;
59+
}
60+
// Count the '\n'.
61+
for (size_t i = 0; i < static_cast<size_t>(bytes); i++) {
62+
if (buffer[i] == '\n') {
63+
++*num_maps;
64+
}
65+
}
66+
}
67+
68+
close(fd);
69+
return true;
70+
}
71+
72+
static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
73+
state.PauseTiming();
74+
// Create a remote process so that the map data is exactly the same.
75+
// Also, so that we can create a set number of maps.
76+
pid_t pid;
77+
if ((pid = fork()) == 0) {
78+
size_t num_maps;
79+
if (!CountMaps(getpid(), &num_maps)) {
80+
exit(1);
81+
}
82+
// Create uniquely named maps.
83+
std::vector<void*> maps;
84+
for (size_t i = num_maps; i < kNumMaps; i++) {
85+
int flags = PROT_READ | PROT_WRITE;
86+
// Alternate page type to make sure a map entry is added for each call.
87+
if ((i % 2) == 0) {
88+
flags |= PROT_EXEC;
89+
}
90+
void* memory = mmap(nullptr, PAGE_SIZE, flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
91+
if (memory == MAP_FAILED) {
92+
fprintf(stderr, "Failed to create map: %s\n", strerror(errno));
93+
exit(1);
94+
}
95+
memset(memory, 0x1, PAGE_SIZE);
96+
if (prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") ==
97+
-1) {
98+
fprintf(stderr, "Failed: %s\n", strerror(errno));
99+
}
100+
maps.push_back(memory);
101+
}
102+
103+
if (!CountMaps(getpid(), &num_maps)) {
104+
exit(1);
105+
}
106+
107+
if (num_maps != kNumMaps) {
108+
fprintf(stderr, "Maps set incorrectly: %zu found, %zu expected.\n", num_maps, kNumMaps);
109+
std::string str;
110+
android::base::ReadFileToString("/proc/self/maps", &str);
111+
fprintf(stderr, "%s\n", str.c_str());
112+
exit(1);
113+
}
114+
115+
// Wait for an hour at most.
116+
sleep(3600);
117+
exit(1);
118+
} else if (pid < 0) {
119+
fprintf(stderr, "Fork failed: %s\n", strerror(errno));
120+
return;
121+
}
122+
123+
size_t num_maps = 0;
124+
for (size_t i = 0; i < 2000; i++) {
125+
if (CountMaps(pid, &num_maps) && num_maps == kNumMaps) {
126+
break;
127+
}
128+
usleep(1000);
129+
}
130+
if (num_maps != kNumMaps) {
131+
fprintf(stderr, "Timed out waiting for the number of maps available: %zu\n", num_maps);
132+
return;
133+
}
134+
135+
state.ResumeTiming();
136+
while (state.KeepRunning()) {
137+
for (size_t i = 0; i < static_cast<size_t>(state.range(0)); i++) {
138+
BacktraceMap* map = map_func(pid, false);
139+
if (map == nullptr) {
140+
fprintf(stderr, "Failed to create map\n");
141+
return;
142+
}
143+
delete map;
144+
}
145+
}
146+
state.PauseTiming();
147+
148+
kill(pid, SIGKILL);
149+
waitpid(pid, nullptr, 0);
150+
}
151+
152+
static void BM_create_map(benchmark::State& state) {
153+
CreateMap(state, BacktraceMap::Create);
154+
}
155+
BENCHMARK(BM_create_map)->Arg(kNumIterations);
156+
157+
static void BM_create_map_new(benchmark::State& state) {
158+
CreateMap(state, BacktraceMap::CreateNew);
159+
}
160+
BENCHMARK(BM_create_map_new)->Arg(kNumIterations);
161+
162+
BENCHMARK_MAIN();

libunwindstack/Maps.cpp

+124-35
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <android-base/unique_fd.h>
2727

28+
#include <cctype>
2829
#include <memory>
2930
#include <string>
3031
#include <vector>
@@ -55,63 +56,151 @@ MapInfo* Maps::Find(uint64_t pc) {
5556
return nullptr;
5657
}
5758

58-
bool Maps::ParseLine(const char* line, MapInfo* map_info) {
59-
char permissions[5];
60-
int name_pos;
61-
// Linux /proc/<pid>/maps lines:
59+
// Assumes that line does not end in '\n'.
60+
static bool InternalParseLine(const char* line, MapInfo* map_info) {
61+
// Do not use a sscanf implementation since it is not performant.
62+
63+
// Example linux /proc/<pid>/maps lines:
6264
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
63-
if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
64-
&map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
65+
char* str;
66+
const char* old_str = line;
67+
map_info->start = strtoul(old_str, &str, 16);
68+
if (old_str == str || *str++ != '-') {
69+
return false;
70+
}
71+
72+
old_str = str;
73+
map_info->end = strtoul(old_str, &str, 16);
74+
if (old_str == str || !std::isspace(*str++)) {
6575
return false;
6676
}
67-
map_info->flags = PROT_NONE;
68-
if (permissions[0] == 'r') {
77+
78+
while (std::isspace(*str)) {
79+
str++;
80+
}
81+
82+
// Parse permissions data.
83+
if (*str == '\0') {
84+
return false;
85+
}
86+
map_info->flags = 0;
87+
if (*str == 'r') {
6988
map_info->flags |= PROT_READ;
89+
} else if (*str != '-') {
90+
return false;
7091
}
71-
if (permissions[1] == 'w') {
92+
str++;
93+
if (*str == 'w') {
7294
map_info->flags |= PROT_WRITE;
95+
} else if (*str != '-') {
96+
return false;
7397
}
74-
if (permissions[2] == 'x') {
98+
str++;
99+
if (*str == 'x') {
75100
map_info->flags |= PROT_EXEC;
101+
} else if (*str != '-') {
102+
return false;
76103
}
104+
str++;
105+
if (*str != 'p' && *str != 's') {
106+
return false;
107+
}
108+
str++;
77109

78-
if (line[name_pos] != '\0') {
79-
map_info->name = &line[name_pos];
80-
size_t length = map_info->name.length() - 1;
81-
if (map_info->name[length] == '\n') {
82-
map_info->name.erase(length);
83-
}
110+
if (!std::isspace(*str++)) {
111+
return false;
112+
}
84113

85-
// Mark a device map in /dev/and not in /dev/ashmem/ specially.
86-
if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
87-
map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
88-
}
114+
old_str = str;
115+
map_info->offset = strtoul(old_str, &str, 16);
116+
if (old_str == str || !std::isspace(*str)) {
117+
return false;
89118
}
90119

120+
// Ignore the 00:00 values.
121+
old_str = str;
122+
(void)strtoul(old_str, &str, 16);
123+
if (old_str == str || *str++ != ':') {
124+
return false;
125+
}
126+
if (std::isspace(*str)) {
127+
return false;
128+
}
129+
130+
// Skip the inode.
131+
old_str = str;
132+
(void)strtoul(str, &str, 16);
133+
if (old_str == str || !std::isspace(*str++)) {
134+
return false;
135+
}
136+
137+
// Skip decimal digit.
138+
old_str = str;
139+
(void)strtoul(old_str, &str, 10);
140+
if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
141+
return false;
142+
}
143+
144+
while (std::isspace(*str)) {
145+
str++;
146+
}
147+
if (*str == '\0') {
148+
map_info->name = str;
149+
return true;
150+
}
151+
152+
// Save the name data.
153+
map_info->name = str;
154+
155+
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
156+
if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
157+
map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
158+
}
91159
return true;
92160
}
93161

94162
bool Maps::Parse() {
95-
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
96-
if (!fp) {
163+
int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
164+
if (fd == -1) {
97165
return false;
98166
}
99167

100-
bool valid = true;
101-
char* line = nullptr;
102-
size_t line_len;
103-
while (getline(&line, &line_len, fp.get()) > 0) {
104-
MapInfo map_info;
105-
if (!ParseLine(line, &map_info)) {
106-
valid = false;
168+
bool return_value = true;
169+
char buffer[2048];
170+
size_t leftover = 0;
171+
while (true) {
172+
ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
173+
if (bytes == -1) {
174+
return_value = false;
175+
break;
176+
}
177+
if (bytes == 0) {
107178
break;
108179
}
180+
bytes += leftover;
181+
char* line = buffer;
182+
while (bytes > 0) {
183+
char* newline = static_cast<char*>(memchr(line, '\n', bytes));
184+
if (newline == nullptr) {
185+
memmove(buffer, line, bytes);
186+
break;
187+
}
188+
*newline = '\0';
109189

110-
maps_.push_back(map_info);
111-
}
112-
free(line);
190+
MapInfo map_info;
191+
if (!InternalParseLine(line, &map_info)) {
192+
return_value = false;
193+
break;
194+
}
195+
maps_.push_back(map_info);
113196

114-
return valid;
197+
bytes -= newline - line + 1;
198+
line = newline + 1;
199+
}
200+
leftover = bytes;
201+
}
202+
close(fd);
203+
return return_value;
115204
}
116205

117206
Maps::~Maps() {
@@ -129,12 +218,12 @@ bool BufferMaps::Parse() {
129218
if (end_of_line == nullptr) {
130219
line = start_of_line;
131220
} else {
132-
end_of_line++;
133221
line = std::string(start_of_line, end_of_line - start_of_line);
222+
end_of_line++;
134223
}
135224

136225
MapInfo map_info;
137-
if (!ParseLine(line.c_str(), &map_info)) {
226+
if (!InternalParseLine(line.c_str(), &map_info)) {
138227
return false;
139228
}
140229
maps_.push_back(map_info);

0 commit comments

Comments
 (0)