Merge "libprocinfo: add functions reading process map file."
This commit is contained in:
commit
0f837fe8ca
16 changed files with 2460 additions and 331 deletions
|
|
@ -91,7 +91,10 @@ cc_library {
|
|||
"libdexfile",
|
||||
],
|
||||
|
||||
static_libs: ["libcutils"],
|
||||
static_libs: [
|
||||
"libcutils",
|
||||
"libprocinfo",
|
||||
],
|
||||
|
||||
// libdexfile will eventually properly export headers, for now
|
||||
// include these directly.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@
|
|||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <backtrace/backtrace_constants.h>
|
||||
#if defined(__linux__)
|
||||
#include <procinfo/process_map.h>
|
||||
#endif
|
||||
|
||||
#include "thread_utils.h"
|
||||
|
||||
|
|
@ -60,27 +63,19 @@ void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
|
|||
*map = {};
|
||||
}
|
||||
|
||||
bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
|
||||
#if defined(__APPLE__)
|
||||
static bool ParseLine(const char* line, backtrace_map_t* map) {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
char permissions[5];
|
||||
int name_pos;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Mac OS vmmap(1) output:
|
||||
// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n",
|
||||
&start, &end, permissions, &name_pos) != 3) {
|
||||
#else
|
||||
// Linux /proc/<pid>/maps lines:
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %*x %*x:%*x %*d %n",
|
||||
&start, &end, permissions, &name_pos) != 3) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -107,24 +102,15 @@ bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
|
|||
map->flags, map->name.c_str());
|
||||
return true;
|
||||
}
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
bool BacktraceMap::Build() {
|
||||
#if defined(__APPLE__)
|
||||
char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
|
||||
#else
|
||||
char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1];
|
||||
#endif
|
||||
char line[1024];
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// cmd is guaranteed to always be big enough to hold this string.
|
||||
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
|
||||
FILE* fp = popen(cmd, "r");
|
||||
#else
|
||||
// path is guaranteed to always be big enough to hold this string.
|
||||
snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
|
||||
FILE* fp = fopen(path, "r");
|
||||
#endif
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -135,13 +121,19 @@ bool BacktraceMap::Build() {
|
|||
maps_.push_back(map);
|
||||
}
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
pclose(fp);
|
||||
#else
|
||||
fclose(fp);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
#else
|
||||
return android::procinfo::ReadProcessMaps(
|
||||
pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
|
||||
maps_.resize(maps_.size() + 1);
|
||||
backtrace_map_t& map = maps_.back();
|
||||
map.start = start;
|
||||
map.end = end;
|
||||
map.flags = flags;
|
||||
map.name = name;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
|
|
|||
|
|
@ -169,8 +169,6 @@ public:
|
|||
|
||||
virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
|
||||
|
||||
virtual bool ParseLine(const char* line, backtrace_map_t* map);
|
||||
|
||||
pid_t pid_;
|
||||
std::deque<backtrace_map_t> maps_;
|
||||
std::vector<std::string> suffixes_to_ignore_;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ cc_library {
|
|||
"HeapWalker.cpp",
|
||||
"LeakFolding.cpp",
|
||||
"LeakPipe.cpp",
|
||||
"LineBuffer.cpp",
|
||||
"MemUnreachable.cpp",
|
||||
"ProcessMappings.cpp",
|
||||
"PtracerThread.cpp",
|
||||
|
|
@ -38,6 +37,7 @@ cc_library {
|
|||
|
||||
static_libs: [
|
||||
"libc_malloc_debug_backtrace",
|
||||
"libprocinfo",
|
||||
],
|
||||
// Only need this for arm since libc++ uses its own unwind code that
|
||||
// doesn't mix with the other default unwind code.
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Copied from system/extras/memory_replay/LineBuffer.cpp
|
||||
// TODO(ccross): find a way to share between libmemunreachable and memory_replay?
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "LineBuffer.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
|
||||
: fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
|
||||
|
||||
bool LineBuffer::GetLine(char** line, size_t* line_len) {
|
||||
while (true) {
|
||||
if (bytes_ > 0) {
|
||||
char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
|
||||
if (newline != nullptr) {
|
||||
*newline = '\0';
|
||||
*line = buffer_ + start_;
|
||||
start_ = newline - buffer_ + 1;
|
||||
bytes_ -= newline - *line + 1;
|
||||
*line_len = newline - *line;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (start_ > 0) {
|
||||
// Didn't find anything, copy the current to the front of the buffer.
|
||||
memmove(buffer_, buffer_ + start_, bytes_);
|
||||
start_ = 0;
|
||||
}
|
||||
ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
|
||||
if (bytes <= 0) {
|
||||
if (bytes_ > 0) {
|
||||
// The read data might not contain a nul terminator, so add one.
|
||||
buffer_[bytes_] = '\0';
|
||||
*line = buffer_ + start_;
|
||||
*line_len = bytes_;
|
||||
bytes_ = 0;
|
||||
start_ = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bytes_ += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBMEMUNREACHABLE_LINE_BUFFER_H
|
||||
#define _LIBMEMUNREACHABLE_LINE_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class LineBuffer {
|
||||
public:
|
||||
LineBuffer(int fd, char* buffer, size_t buffer_len);
|
||||
|
||||
bool GetLine(char** line, size_t* line_len);
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
char* buffer_ = nullptr;
|
||||
size_t buffer_len_ = 0;
|
||||
size_t start_ = 0;
|
||||
size_t bytes_ = 0;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
|
||||
|
|
@ -14,21 +14,30 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include "LineBuffer.h"
|
||||
#include "ProcessMappings.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
// This function is not re-entrant since it uses a static buffer for
|
||||
// the line data.
|
||||
struct ReadMapCallback {
|
||||
ReadMapCallback(allocator::vector<Mapping>& mappings) : mappings_(mappings) {}
|
||||
|
||||
void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) const {
|
||||
mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
|
||||
name);
|
||||
}
|
||||
|
||||
allocator::vector<Mapping>& mappings_;
|
||||
};
|
||||
|
||||
bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
|
||||
char map_buffer[1024];
|
||||
snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
|
||||
|
|
@ -36,35 +45,13 @@ bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
|
|||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
|
||||
char* line;
|
||||
size_t line_len;
|
||||
while (line_buf.GetLine(&line, &line_len)) {
|
||||
int name_pos;
|
||||
char perms[5];
|
||||
Mapping mapping{};
|
||||
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
|
||||
&mapping.end, perms, &name_pos) == 3) {
|
||||
if (perms[0] == 'r') {
|
||||
mapping.read = true;
|
||||
}
|
||||
if (perms[1] == 'w') {
|
||||
mapping.write = true;
|
||||
}
|
||||
if (perms[2] == 'x') {
|
||||
mapping.execute = true;
|
||||
}
|
||||
if (perms[3] == 'p') {
|
||||
mapping.priv = true;
|
||||
}
|
||||
if ((size_t)name_pos < line_len) {
|
||||
strlcpy(mapping.name, line + name_pos, sizeof(mapping.name));
|
||||
}
|
||||
mappings.emplace_back(mapping);
|
||||
}
|
||||
allocator::string content(mappings.get_allocator());
|
||||
ssize_t n;
|
||||
while ((n = TEMP_FAILURE_RETRY(read(fd, map_buffer, sizeof(map_buffer)))) > 0) {
|
||||
content.append(map_buffer, n);
|
||||
}
|
||||
return true;
|
||||
ReadMapCallback callback(mappings);
|
||||
return android::procinfo::ReadMapFileContent(&content[0], callback);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#ifndef LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
|
||||
#define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Allocator.h"
|
||||
|
||||
namespace android {
|
||||
|
|
@ -27,8 +29,13 @@ struct Mapping {
|
|||
bool read;
|
||||
bool write;
|
||||
bool execute;
|
||||
bool priv;
|
||||
char name[96];
|
||||
|
||||
Mapping() {}
|
||||
Mapping(uintptr_t begin, uintptr_t end, bool read, bool write, bool execute, const char* name)
|
||||
: begin(begin), end(end), read(read), write(write), execute(execute) {
|
||||
strlcpy(this->name, name, sizeof(this->name));
|
||||
}
|
||||
};
|
||||
|
||||
// This function is not re-entrant since it uses a static buffer for
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ cc_test {
|
|||
host_supported: true,
|
||||
srcs: [
|
||||
"process_test.cpp",
|
||||
"process_map_test.cpp",
|
||||
],
|
||||
target: {
|
||||
darwin: {
|
||||
|
|
@ -84,5 +85,45 @@ cc_test {
|
|||
},
|
||||
},
|
||||
|
||||
data: [
|
||||
"testdata/*",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
|
||||
cc_benchmark {
|
||||
name: "libprocinfo_benchmark",
|
||||
defaults: ["libprocinfo_defaults"],
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"process_map_benchmark.cpp",
|
||||
],
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
windows: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
shared_libs: [
|
||||
"libbacktrace",
|
||||
"libbase",
|
||||
"libprocinfo",
|
||||
"libunwindstack",
|
||||
],
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
|
||||
data: [
|
||||
"testdata/*",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
151
libprocinfo/include/procinfo/process_map.h
Normal file
151
libprocinfo/include/procinfo/process_map.h
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
namespace android {
|
||||
namespace procinfo {
|
||||
|
||||
template <class CallbackType>
|
||||
bool ReadMapFileContent(char* content, const CallbackType& callback) {
|
||||
uint64_t start_addr;
|
||||
uint64_t end_addr;
|
||||
uint16_t flags;
|
||||
uint64_t pgoff;
|
||||
char* next_line = content;
|
||||
char* p;
|
||||
|
||||
auto pass_space = [&]() {
|
||||
if (*p != ' ') {
|
||||
return false;
|
||||
}
|
||||
while (*p == ' ') {
|
||||
p++;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto pass_xdigit = [&]() {
|
||||
if (!isxdigit(*p)) {
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
p++;
|
||||
} while (isxdigit(*p));
|
||||
return true;
|
||||
};
|
||||
|
||||
while (next_line != nullptr && *next_line != '\0') {
|
||||
p = next_line;
|
||||
next_line = strchr(next_line, '\n');
|
||||
if (next_line != nullptr) {
|
||||
*next_line = '\0';
|
||||
next_line++;
|
||||
}
|
||||
// Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
|
||||
char* end;
|
||||
// start_addr
|
||||
start_addr = strtoull(p, &end, 16);
|
||||
if (end == p || *end != '-') {
|
||||
return false;
|
||||
}
|
||||
p = end + 1;
|
||||
// end_addr
|
||||
end_addr = strtoull(p, &end, 16);
|
||||
if (end == p) {
|
||||
return false;
|
||||
}
|
||||
p = end;
|
||||
if (!pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// flags
|
||||
flags = 0;
|
||||
if (*p == 'r') {
|
||||
flags |= PROT_READ;
|
||||
} else if (*p != '-') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (*p == 'w') {
|
||||
flags |= PROT_WRITE;
|
||||
} else if (*p != '-') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (*p == 'x') {
|
||||
flags |= PROT_EXEC;
|
||||
} else if (*p != '-') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (*p != 'p' && *p != 's') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (!pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// pgoff
|
||||
pgoff = strtoull(p, &end, 16);
|
||||
if (end == p) {
|
||||
return false;
|
||||
}
|
||||
p = end;
|
||||
if (!pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// major:minor
|
||||
if (!pass_xdigit() || *p++ != ':' || !pass_xdigit() || !pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// inode
|
||||
if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
|
||||
return false;
|
||||
}
|
||||
// filename
|
||||
callback(start_addr, end_addr, flags, pgoff, p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ReadMapFile(
|
||||
const std::string& map_file,
|
||||
const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(map_file, &content)) {
|
||||
return false;
|
||||
}
|
||||
return ReadMapFileContent(&content[0], callback);
|
||||
}
|
||||
|
||||
inline bool ReadProcessMaps(
|
||||
pid_t pid,
|
||||
const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
|
||||
return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
|
||||
}
|
||||
|
||||
} /* namespace procinfo */
|
||||
} /* namespace android */
|
||||
85
libprocinfo/process_map_benchmark.cpp
Normal file
85
libprocinfo/process_map_benchmark.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
struct MapInfo {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint16_t flags;
|
||||
uint64_t pgoff;
|
||||
const std::string name;
|
||||
|
||||
MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
|
||||
: start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
|
||||
};
|
||||
|
||||
static void BM_ReadMapFile(benchmark::State& state) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
for (auto _ : state) {
|
||||
std::vector<MapInfo> maps;
|
||||
android::procinfo::ReadMapFile(
|
||||
map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
|
||||
const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
|
||||
CHECK_EQ(maps.size(), 2043u);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ReadMapFile);
|
||||
|
||||
static void BM_unwindstack_FileMaps(benchmark::State& state) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
for (auto _ : state) {
|
||||
unwindstack::FileMaps maps(map_file);
|
||||
maps.Parse();
|
||||
CHECK_EQ(maps.Total(), 2043u);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_unwindstack_FileMaps);
|
||||
|
||||
static void BM_unwindstack_BufferMaps(benchmark::State& state) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
std::string content;
|
||||
CHECK(android::base::ReadFileToString(map_file, &content));
|
||||
for (auto _ : state) {
|
||||
unwindstack::BufferMaps maps(content.c_str());
|
||||
maps.Parse();
|
||||
CHECK_EQ(maps.Total(), 2043u);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_unwindstack_BufferMaps);
|
||||
|
||||
static void BM_backtrace_BacktraceMap(benchmark::State& state) {
|
||||
pid_t pid = getpid();
|
||||
for (auto _ : state) {
|
||||
BacktraceMap* map = BacktraceMap::Create(pid, true);
|
||||
CHECK(map != nullptr);
|
||||
delete map;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_backtrace_BacktraceMap);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
60
libprocinfo/process_map_test.cpp
Normal file
60
libprocinfo/process_map_test.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
struct MapInfo {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint16_t flags;
|
||||
uint64_t pgoff;
|
||||
const std::string name;
|
||||
|
||||
MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
|
||||
: start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
|
||||
};
|
||||
|
||||
TEST(process_map, smoke) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
std::vector<MapInfo> maps;
|
||||
ASSERT_TRUE(android::procinfo::ReadMapFile(
|
||||
map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
|
||||
const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
|
||||
ASSERT_EQ(2043u, maps.size());
|
||||
ASSERT_EQ(maps[0].start, 0x12c00000ULL);
|
||||
ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
|
||||
ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
|
||||
ASSERT_EQ(maps[0].pgoff, 0ULL);
|
||||
ASSERT_EQ(maps[0].name, "/dev/ashmem/dalvik-main space (region space) (deleted)");
|
||||
ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
|
||||
ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
|
||||
ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
|
||||
ASSERT_EQ(maps[876].pgoff, 0ULL);
|
||||
ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
|
||||
ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
|
||||
ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
|
||||
ASSERT_EQ(maps[1260].flags, PROT_READ);
|
||||
ASSERT_EQ(maps[1260].pgoff, 0ULL);
|
||||
ASSERT_EQ(maps[1260].name,
|
||||
"/dev/ashmem/dalvik-classes.dex extracted in memory from "
|
||||
"/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)");
|
||||
}
|
||||
2043
libprocinfo/testdata/maps
vendored
Normal file
2043
libprocinfo/testdata/maps
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -110,6 +110,10 @@ cc_library {
|
|||
},
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libprocinfo",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libdexfile",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
|
@ -57,150 +58,16 @@ MapInfo* Maps::Find(uint64_t pc) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Assumes that line does not end in '\n'.
|
||||
static MapInfo* InternalParseLine(const char* line) {
|
||||
// Do not use a sscanf implementation since it is not performant.
|
||||
|
||||
// Example linux /proc/<pid>/maps lines:
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
|
||||
char* str;
|
||||
const char* old_str = line;
|
||||
uint64_t start = strtoull(old_str, &str, 16);
|
||||
if (old_str == str || *str++ != '-') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
old_str = str;
|
||||
uint64_t end = strtoull(old_str, &str, 16);
|
||||
if (old_str == str || !std::isspace(*str++)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (std::isspace(*str)) {
|
||||
str++;
|
||||
}
|
||||
|
||||
// Parse permissions data.
|
||||
if (*str == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
uint16_t flags = 0;
|
||||
if (*str == 'r') {
|
||||
flags |= PROT_READ;
|
||||
} else if (*str != '-') {
|
||||
return nullptr;
|
||||
}
|
||||
str++;
|
||||
if (*str == 'w') {
|
||||
flags |= PROT_WRITE;
|
||||
} else if (*str != '-') {
|
||||
return nullptr;
|
||||
}
|
||||
str++;
|
||||
if (*str == 'x') {
|
||||
flags |= PROT_EXEC;
|
||||
} else if (*str != '-') {
|
||||
return nullptr;
|
||||
}
|
||||
str++;
|
||||
if (*str != 'p' && *str != 's') {
|
||||
return nullptr;
|
||||
}
|
||||
str++;
|
||||
|
||||
if (!std::isspace(*str++)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
old_str = str;
|
||||
uint64_t offset = strtoull(old_str, &str, 16);
|
||||
if (old_str == str || !std::isspace(*str)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ignore the 00:00 values.
|
||||
old_str = str;
|
||||
(void)strtoull(old_str, &str, 16);
|
||||
if (old_str == str || *str++ != ':') {
|
||||
return nullptr;
|
||||
}
|
||||
if (std::isspace(*str)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip the inode.
|
||||
old_str = str;
|
||||
(void)strtoull(str, &str, 16);
|
||||
if (old_str == str || !std::isspace(*str++)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip decimal digit.
|
||||
old_str = str;
|
||||
(void)strtoull(old_str, &str, 10);
|
||||
if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (std::isspace(*str)) {
|
||||
str++;
|
||||
}
|
||||
if (*str == '\0') {
|
||||
return new MapInfo(start, end, offset, flags, "");
|
||||
}
|
||||
|
||||
// Save the name data.
|
||||
std::string name(str);
|
||||
|
||||
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
|
||||
if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
|
||||
flags |= MAPS_FLAGS_DEVICE_MAP;
|
||||
}
|
||||
return new MapInfo(start, end, offset, flags, name);
|
||||
}
|
||||
|
||||
bool Maps::Parse() {
|
||||
int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool return_value = true;
|
||||
char buffer[2048];
|
||||
size_t leftover = 0;
|
||||
while (true) {
|
||||
ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
|
||||
if (bytes == -1) {
|
||||
return_value = false;
|
||||
break;
|
||||
}
|
||||
if (bytes == 0) {
|
||||
break;
|
||||
}
|
||||
bytes += leftover;
|
||||
char* line = buffer;
|
||||
while (bytes > 0) {
|
||||
char* newline = static_cast<char*>(memchr(line, '\n', bytes));
|
||||
if (newline == nullptr) {
|
||||
memmove(buffer, line, bytes);
|
||||
break;
|
||||
}
|
||||
*newline = '\0';
|
||||
|
||||
MapInfo* map_info = InternalParseLine(line);
|
||||
if (map_info == nullptr) {
|
||||
return_value = false;
|
||||
break;
|
||||
}
|
||||
maps_.push_back(map_info);
|
||||
|
||||
bytes -= newline - line + 1;
|
||||
line = newline + 1;
|
||||
}
|
||||
leftover = bytes;
|
||||
}
|
||||
close(fd);
|
||||
return return_value;
|
||||
return android::procinfo::ReadMapFile(
|
||||
GetMapsFile(),
|
||||
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
|
||||
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
|
||||
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
|
||||
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
|
||||
}
|
||||
maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
|
||||
});
|
||||
}
|
||||
|
||||
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
|
||||
|
|
@ -222,26 +89,16 @@ Maps::~Maps() {
|
|||
}
|
||||
|
||||
bool BufferMaps::Parse() {
|
||||
const char* start_of_line = buffer_;
|
||||
do {
|
||||
std::string line;
|
||||
const char* end_of_line = strchr(start_of_line, '\n');
|
||||
if (end_of_line == nullptr) {
|
||||
line = start_of_line;
|
||||
} else {
|
||||
line = std::string(start_of_line, end_of_line - start_of_line);
|
||||
end_of_line++;
|
||||
}
|
||||
|
||||
MapInfo* map_info = InternalParseLine(line.c_str());
|
||||
if (map_info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
maps_.push_back(map_info);
|
||||
|
||||
start_of_line = end_of_line;
|
||||
} while (start_of_line != nullptr && *start_of_line != '\0');
|
||||
return true;
|
||||
std::string content(buffer_);
|
||||
return android::procinfo::ReadMapFileContent(
|
||||
&content[0],
|
||||
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
|
||||
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
|
||||
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
|
||||
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
|
||||
}
|
||||
maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
|
||||
});
|
||||
}
|
||||
|
||||
const std::string RemoteMaps::GetMapsFile() const {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,13 @@ class Memory;
|
|||
struct MapInfo {
|
||||
MapInfo() = default;
|
||||
MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
|
||||
MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
|
||||
: start(start),
|
||||
end(end),
|
||||
offset(offset),
|
||||
flags(flags),
|
||||
name(name),
|
||||
load_bias(static_cast<uint64_t>(-1)) {}
|
||||
MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
|
||||
: start(start),
|
||||
end(end),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue