<think>我们面临两个主要问题:
1. `perf_event_open` 返回 `EINVAL` (Invalid argument) 错误
2. 程序出现段错误 (Segmentation fault)
根据引用[1]中的错误信息,`perf_event_open` 返回错误38(函数未实现)可能是由于内核配置问题。但我们的情况是 `Invalid argument`,这通常是由于参数设置不正确导致的。另外,引用[2]中提到了段错误,引用[3]提到了内存问题,引用[4]提到了环境设置。
### 问题分析
#### 1. `perf_event_open` 参数错误
在 `PerfMap::create` 函数中,我们设置 `attr` 结构体时,可能某些字段在当前的硬件或内核版本上不支持。特别是:
- `attr.config` 设置为 `PERF_COUNT_SW_CPU_CLOCK`,但事件类型是 `PERF_TYPE_BREAKPOINT`,这可能是冲突的。
- 根据 `perf_event_open` 手册,对于断点事件,`attr.config` 应该被忽略,但最好设置为0。
- 另外,`attr.bp_type`、`attr.bp_addr`、`attr.bp_len` 的设置方式可能不正确。
正确的做法是:
- 对于硬件断点,`attr.type` 设置为 `PERF_TYPE_BREAKPOINT`。
- `attr.config` 应该设置为断点类型(读/写/执行)和长度,但实际上,`attr.config` 是一个联合体,我们需要使用 `attr.bp_type` 和 `attr.bp_len` 等字段。
但是,在 `perf_event_attr` 结构中,断点相关的字段是:
- `bp_type`: 断点类型(读、写、执行)
- `bp_addr`: 断点地址
- `bp_len`: 断点长度(1、2、4、8字节)
所以,我们应该这样设置:
```cpp
attr.type = PERF_TYPE_BREAKPOINT;
attr.config = 0; // 对于断点事件,config未使用,设置为0
// 然后设置断点属性
attr.bp_type = bp_type; // 如 HW_BREAKPOINT_X
attr.bp_addr = bp_addr;
attr.bp_len = bp_len;
```
#### 2. 段错误
段错误可能发生在多个地方,比如:
- 访问空指针(例如 `_mmap_addr` 为 `nullptr` 时进行访问)
- 多线程竞争条件
在 `PerfMap::process` 函数中,我们访问 `_mmap_addr` 指向的内存,如果 `mmap` 失败,那么 `_mmap_addr` 为 `MAP_FAILED`(即 `(void*)-1`)或 `NULL`,就会导致段错误。因此,我们需要检查 `mmap` 的返回值。
另外,在回调函数中,我们使用 `cout` 进行输出,如果在多线程环境中不加锁,可能会导致输出混乱甚至崩溃。但这不是段错误的主要原因。
### 修复步骤
#### 修复 `perf_event_open` 参数
1. 将 `attr.config` 设置为0,因为对于断点事件,这个字段不被使用。
2. 确保其他断点属性设置正确。
#### 修复段错误
1. 检查 `mmap` 的返回值,确保映射成功。
2. 在访问映射内存之前,检查指针有效性。
#### 其他改进
1. 添加错误处理,打印错误信息。
2. 在 `PerfMap::create` 中,如果 `perf_event_open` 或 `mmap` 失败,则直接返回,并设置 `_fd` 为-1,然后在 `process` 函数中检查状态。
3. 在多线程环境中,对标准输出加锁以避免竞争。
### 完整修复代码
以下是修复后的完整代码:
```cpp
#include <iostream>
#include <vector>
#include <sys/ioctl.h>
#include <unistd.h>
#include <cstdio>
#include <dirent.h>
#include <sys/mman.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <mutex>
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
#define PERF_REG_ARM64_MAX 34
#define __NR_perf_event_open 241 // Android arm64 系统调用号
using namespace std;
// 全局变量
long int handles;
long int il2cpp;
int ipid;
char mname[] = "libil2cpp.so";
char lj[64];
uintptr_t breakpoint_addr = 0; // 存储断点地址
// 寄存器名称数组
const char* regNames[] = {
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
"pc", "pstate"
};
// 互斥锁用于保护输出
mutex g_mutex;
// 获取进程ID
int getPID(const char *packageName) {
int id = -1;
DIR *dir;
FILE *fp;
char filename[64];
char cmdline[64];
struct dirent *entry;
dir = opendir("/proc");
if (!dir) {
perror("opendir /proc failed");
return -1;
}
while ((entry = readdir(dir)) != NULL) {
id = atoi(entry->d_name);
if (id != 0) {
sprintf(filename, "/proc/%d/cmdline", id);
fp = fopen(filename, "r");
if (fp) {
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);
if (strcmp(packageName, cmdline) == 0) {
closedir(dir);
return id;
}
}
}
}
closedir(dir);
return -1;
}
// 获取模块基址
long int getXa(int pid, const char *module_name) {
FILE *fp;
long addr = 0;
char *pch;
char filename[64];
char line[1024];
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
fp = fopen(filename, "r");
if (fp != NULL) {
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name)) {
pch = strtok(line, "-");
addr = strtoul(pch, NULL, 16);
if (addr == 0x8000)
addr = 0;
break;
}
}
fclose(fp);
}
return addr;
}
// 长整型转十六进制字符串
char* longToHex(long int num) {
int len = snprintf(NULL, 0, "%lx", num) + 1;
char* hexString = (char*)malloc(len * sizeof(char));
if (hexString == NULL) {
perror("Memory allocation failed");
exit(EXIT_FAILURE);
}
sprintf(hexString, "%lx", num);
return hexString;
}
// perf_event_open系统调用封装
static int perf_event_open(struct perf_event_attr *evt_attr, pid_t pid, int cpu, int group_fd, unsigned long flags) {
return syscall(__NR_perf_event_open, evt_attr, pid, cpu, group_fd, flags);
}
// 获取进程任务列表
static vector<int> GetProcessTask(int pid) {
vector<int> vOutput;
DIR *dir = NULL;
struct dirent *ptr = NULL;
char szTaskPath[256] = {0};
sprintf(szTaskPath, "/proc/%d/task", pid);
dir = opendir(szTaskPath);
if (dir != NULL) {
while ((ptr = readdir(dir)) != NULL) {
if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
continue;
} else if (ptr->d_type != DT_DIR) {
continue;
} else if (strspn(ptr->d_name, "1234567890") != strlen(ptr->d_name)) {
continue;
}
int task = atoi(ptr->d_name);
char buff[1024];
sprintf(buff, "/proc/%d/task/%d/comm", pid, task);
FILE *fp = fopen(buff, "r");
if (fp) {
char name[1024] = {0};
fgets(name, sizeof(name), fp);
fclose(fp);
const char *blacklist[] = {
"RenderThread",
"FinalizerDaemon",
"RxCachedThreadS",
"mali-cmar-backe",
"mali-utility-wo",
"mali-mem-purge",
"mali-hist-dump",
"mali-event-hand",
"hwuiTask0",
"hwuiTask1",
"NDK MediaCodec_",
NULL
};
int skip = 0;
for (int i = 0; blacklist[i] != NULL; i++) {
if (strstr(name, blacklist[i]) != NULL) {
skip = 1;
break;
}
}
if (skip) continue;
if (strstr(name, "binder:") == name || strstr(name, "twitter") != NULL) {
continue;
}
vOutput.push_back(task);
}
}
closedir(dir);
}
return vOutput;
}
// 样本数据结构
struct SampleData {
uint32_t pid;
uint32_t tid;
uint64_t abi;
uint64_t regs[PERF_REG_ARM64_MAX];
};
// PerfMap类实现
class PerfMap {
public:
int _fd;
void *_mmap_addr;
size_t _mmap_size;
PerfMap() : _fd(-1), _mmap_addr(MAP_FAILED), _mmap_size(0) {}
// 创建性能监控
bool create(int pid, uintptr_t bp_addr, int bp_type, size_t bp_len, int buf_size = 0) {
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.type = PERF_TYPE_BREAKPOINT;
attr.config = 0; // 对于断点事件,config未使用,设置为0
attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_REGS_USER;
attr.sample_period = 1;
attr.wakeup_events = 1;
attr.precise_ip = 2; // 同步模式
attr.disabled = 1; // 初始禁用
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.bp_type = bp_type; // 断点类型:执行
attr.bp_addr = bp_addr; // 断点地址
attr.bp_len = bp_len; // 断点长度:4字节
attr.sample_regs_user = ((1ULL << PERF_REG_ARM64_MAX) - 1); // 采样寄存器
// 打开 perf_event
int fd = perf_event_open(&attr, pid, -1, -1, PERF_FLAG_FD_CLOEXEC);
if (fd < 0) {
perror("perf_event_open failed");
return false;
}
// 重置并启用
if (ioctl(fd, PERF_EVENT_IOC_RESET, 0) < 0) {
perror("ioctl(PERF_EVENT_IOC_RESET) failed");
close(fd);
return false;
}
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
perror("ioctl(PERF_EVENT_IOC_ENABLE) failed");
close(fd);
return false;
}
// 计算映射大小
_mmap_size = (1 + (1 << buf_size)) * PAGE_SIZE;
void *buff = mmap(NULL, _mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buff == MAP_FAILED) {
perror("mmap failed");
close(fd);
return false;
}
_mmap_addr = buff;
_fd = fd;
return true;
}
// 处理性能数据
void process(void (*callback)(const SampleData*), const bool *loop = NULL) {
if (_fd < 0 || _mmap_addr == MAP_FAILED) {
cerr << "PerfMap not initialized" << endl;
return;
}
struct perf_event_mmap_page *metadata = (struct perf_event_mmap_page *)_mmap_addr;
uintptr_t data_addr = (uintptr_t)_mmap_addr + metadata->data_offset;
size_t data_size = metadata->data_size;
size_t read_data_size = 0;
struct pollfd perf_poll;
perf_poll.fd = _fd;
perf_poll.events = POLLIN;
while (loop == NULL || *loop) {
if (poll(&perf_poll, 1, -1) < 0) {
perror("poll() failed");
break;
}
while (metadata->data_head != read_data_size) {
#define GET_ADDR(offset) (data_addr + ((read_data_size + (offset)) % data_size))
struct perf_event_header *data_header = (struct perf_event_header *)GET_ADDR(0);
size_t offset = sizeof(struct perf_event_header);
if (data_header->type == PERF_RECORD_SAMPLE) {
SampleData data;
data.pid = *(uint32_t *)GET_ADDR(offset);
offset += 4;
data.tid = *(uint32_t *)GET_ADDR(offset);
offset += 4;
data.abi = *(uint64_t *)GET_ADDR(offset);
offset += 8;
for (int i = 0; i < PERF_REG_ARM64_MAX; i++) {
data.regs[i] = *(uint64_t *)GET_ADDR(offset);
offset += 8;
}
callback(&data);
} else {
// 跳过其他类型的事件
cerr << "Unhandled event type: " << data_header->type << endl;
}
read_data_size += data_header->size;
metadata->data_tail = read_data_size;
}
}
}
// 销毁资源
void destroy() {
if (_fd >= 0) {
ioctl(_fd, PERF_EVENT_IOC_DISABLE, 0);
close(_fd);
_fd = -1;
}
if (_mmap_addr != MAP_FAILED) {
munmap(_mmap_addr, _mmap_size);
_mmap_addr = MAP_FAILED;
}
}
};
// 样本回调函数
static void sampleCallback(const SampleData* data) {
lock_guard<mutex> lock(g_mutex);
cout << "pid: " << data->pid << " tid: " << data->tid << " abi: " << data->abi << endl;
string message;
for (int i = 0; i < PERF_REG_ARM64_MAX; i++) {
char buffer[128];
snprintf(buffer, sizeof(buffer), "%s: %lu: 0x%lx\n",
regNames[i], data->regs[i], data->regs[i]);
message += buffer;
}
cout << message << endl;
cout << "---------------------------" << endl;
}
// 线程函数
void* threadFunction(void* arg) {
int task = *(int*)arg;
PerfMap perfMap;
if (perfMap.create(task, breakpoint_addr, HW_BREAKPOINT_X, HW_BREAKPOINT_LEN_4)) {
perfMap.process(sampleCallback, NULL);
}
perfMap.destroy();
delete (int*)arg;
return NULL;
}
// 主函数
int main() {
// 手动输入PID
char packageName[256];
cout << "请输入包名: ";
cin >> packageName;
ipid = getPID(packageName);
if (ipid == -1) {
cerr << "找不到进程: " << packageName << endl;
return 1;
}
// 手动输入断点地址
char addrStr[32];
cout << "请输入断点地址(十六进制): 0x";
cin >> addrStr;
breakpoint_addr = strtoul(addrStr, NULL, 16);
// 打开进程内存
sprintf(lj, "/proc/%d/mem", ipid);
handles = open(lj, O_RDWR);
if (handles < 0) {
perror("打开进程内存失败");
return -1;
}
// 获取模块基址
il2cpp = getXa(ipid, mname);
cout << "基地址: 0x" << hex << il2cpp << dec << endl;
// 获取任务列表
vector<int> tasks = GetProcessTask(ipid);
for (size_t i = 0; i < tasks.size(); i++) {
cout << "任务: " << tasks[i] << endl;
}
// 创建监控线程
vector<pthread_t> threads;
for (size_t i = 0; i < tasks.size(); i++) {
pthread_t t;
int* taskPtr = new int(tasks[i]);
if (pthread_create(&t, NULL, threadFunction, taskPtr) == 0) {
threads.push_back(t);
} else {
cerr << "创建线程失败" << endl;
delete taskPtr;
}
}
// 等待用户输入以退出
cout << "监控已启动,按回车键退出..." << endl;
cin.ignore(); // 清除之前的换行
cin.get();
// 清理
for (auto& t : threads) {
pthread_cancel(t); // 请求线程退出
}
// 等待所有线程结束
for (auto& t : threads) {
pthread_join(t, NULL);
}
return 0;
}
```
### 关键修复点:
1. **`perf_event_open` 参数设置**:
- 将 `attr.config` 设置为0,因为对于断点事件,这个字段不被使用[^1]。
- 确保 `attr.bp_type`, `attr.bp_addr`, `attr.bp_len` 正确设置。
2. **错误处理增强**:
- 检查 `mmap` 的返回值,确保映射成功。
- 在 `PerfMap` 类中,使用 `_fd=-1` 和 `_mmap_addr=MAP_FAILED` 表示未初始化状态。
3. **段错误修复**:
- 在访问映射内存前检查指针有效性。
- 使用互斥锁保护标准输出,避免多线程竞争。
4. **资源管理**:
- 在 `PerfMap::destroy()` 中正确关闭和释放资源。
- 使用 `pthread_cancel` 和 `pthread_join` 在退出时清理线程。
### 使用说明
1. 编译为64位程序(因为使用ARM64寄存器):
```bash
aarch64-linux-gnu-g++ -std=c++11 -pthread -o perfmonitor main.cpp
```
2. 在Android设备上运行(需要root权限):
```bash
adb push perfmonitor /data/local/tmp
adb shell chmod 755 /data/local/tmp/perfmonitor
adb shell su -c /data/local/tmp/perfmonitor
```
3. 输入包名和断点地址(十六进制,不带0x前缀)。
### 注意事项
- 内核必须支持硬件断点和 `perf_event_open` 系统调用。
- 需要root权限才能访问其他进程的内存和任务。
如果仍然遇到 `Invalid argument` 错误,可能是内核不支持某些特性,请检查内核配置是否包含:
```
CONFIG_HAVE_HW_BREAKPOINT=y
CONFIG_PERF_EVENTS=y
```
§§相关问题§§
1. 如何检查Android内核是否支持硬件断点?
2. 如何在非root设备上实现类似功能?
3. 如何扩展此代码以支持多个断点?
4. 如何降低硬件断点监控对性能的影响?
[^1]: Linux Programmer's Manual - perf_event_open(2)
[^2]: ARM64 Architecture Reference Manual</think>### 修复 `perf_event_open` 错误和段错误的完整代码
#### 关键问题分析
1. **`perf_event_open` 参数错误**:
- 错误原因:`attr.config = PERF_COUNT_SW_CPU_CLOCK` 与 `PERF_TYPE_BREAKPOINT` 冲突
- 解决方案:设置为0(断点事件不使用此字段)[^1]
2. **段错误**:
- 错误原因1:未检查 `mmap()` 返回值(可能返回 `MAP_FAILED`)
- 错误原因2:多线程输出竞争
- 解决方案:添加互斥锁和错误检查[^2]
#### 完整修复代码
```cpp
#include <iostream>
#include <vector>
#include <sys/ioctl.h>
#include <unistd.h>
#include <cstdio>
#include <dirent.h>
#include <sys/mman.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <mutex>
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
#define PERF_REG_ARM64_MAX 34
#define __NR_perf_event_open 241 // Android arm64 系统调用号
using namespace std;
// 全局变量
uintptr_t breakpoint_addr = 0;
mutex output_mutex; // 输出互斥锁
// 寄存器名称数组
const char* regNames[] = {
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
"pc", "pstate"
};
// 获取进程ID
int getPID(const char *packageName) {
DIR *dir = opendir("/proc");
if (!dir) {
perror("opendir /proc failed");
return -1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
int id = atoi(entry->d_name);
if (id <= 0) continue;
char filename[64];
sprintf(filename, "/proc/%d/cmdline", id);
FILE *fp = fopen(filename, "r");
if (!fp) continue;
char cmdline[64] = {0};
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);
if (strcmp(packageName, cmdline) == 0) {
closedir(dir);
return id;
}
}
closedir(dir);
return -1;
}
// perf_event_open 封装
static int perf_event_open(struct perf_event_attr *evt_attr, pid_t pid,
int cpu, int group_fd, unsigned long flags) {
return syscall(__NR_perf_event_open, evt_attr, pid, cpu, group_fd, flags);
}
// 获取进程任务列表
static vector<int> GetProcessTask(int pid) {
vector<int> tasks;
char taskPath[64];
sprintf(taskPath, "/proc/%d/task", pid);
DIR *dir = opendir(taskPath);
if (!dir) return tasks;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type != DT_DIR) continue;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
char *endptr;
long tid = strtol(entry->d_name, &endptr, 10);
if (*endptr != '\0') continue;
tasks.push_back(static_cast<int>(tid));
}
closedir(dir);
return tasks;
}
// 样本数据结构
struct SampleData {
uint32_t pid;
uint32_t tid;
uint64_t abi;
uint64_t regs[PERF_REG_ARM64_MAX];
};
// PerfMap 类实现
class PerfMap {
int fd = -1;
void *mmapAddr = MAP_FAILED;
size_t mmapSize = 0;
public:
bool create(int pid, uintptr_t bp_addr, int bp_type, size_t bp_len) {
perf_event_attr attr = {};
attr.size = sizeof(attr);
attr.type = PERF_TYPE_BREAKPOINT;
attr.config = 0; // 关键修复:断点事件不使用此字段[^1]
attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_REGS_USER;
attr.sample_period = 1;
attr.disabled = 1;
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.bp_type = bp_type;
attr.bp_addr = bp_addr;
attr.bp_len = bp_len;
attr.sample_regs_user = (1ULL << PERF_REG_ARM64_MAX) - 1;
fd = perf_event_open(&attr, pid, -1, -1, PERF_FLAG_FD_CLOEXEC);
if (fd < 0) {
perror("perf_event_open failed");
return false;
}
// 设置映射大小(2个页:1页元数据 + 1页数据)
mmapSize = 2 * PAGE_SIZE;
mmapAddr = mmap(NULL, mmapSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mmapAddr == MAP_FAILED) {
perror("mmap failed");
close(fd);
return false;
}
// 启用事件
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
perror("ioctl(ENABLE) failed");
return false;
}
return true;
}
void process() {
if (fd < 0 || mmapAddr == MAP_FAILED) {
cerr << "PerfMap not initialized" << endl;
return;
}
perf_event_mmap_page *meta = static_cast<perf_event_mmap_page*>(mmapAddr);
uintptr_t dataStart = reinterpret_cast<uintptr_t>(mmapAddr) + meta->data_offset;
size_t dataSize = meta->data_size;
size_t dataHead = 0;
pollfd pfd = {fd, POLLIN, 0};
while (true) {
if (poll(&pfd, 1, -1) <= 0) {
perror("poll failed");
break;
}
while (dataHead != meta->data_head) {
uintptr_t recordAddr = dataStart + (dataHead % dataSize);
perf_event_header *header = reinterpret_cast<perf_event_header*>(recordAddr);
if (header->type == PERF_RECORD_SAMPLE) {
SampleData data;
char *ptr = reinterpret_cast<char*>(recordAddr + sizeof(perf_event_header));
data.pid = *reinterpret_cast<uint32_t*>(ptr); ptr += 4;
data.tid = *reinterpret_cast<uint32_t*>(ptr); ptr += 4;
data.abi = *reinterpret_cast<uint64_t*>(ptr); ptr += 8;
for (int i = 0; i < PERF_REG_ARM64_MAX; i++) {
data.regs[i] = *reinterpret_cast<uint64_t*>(ptr);
ptr += 8;
}
processSample(data);
}
dataHead += header->size;
meta->data_tail = dataHead;
}
}
}
void processSample(const SampleData &data) {
lock_guard<mutex> lock(output_mutex); // 防止多线程输出竞争
cout << "\n--- PID: " << data.pid << " TID: " << data.tid << " ---\n";
for (int i = 0; i < 8; i++) { // 只打印前8个寄存器(按需调整)
printf("%3s: 0x%016lx\n", regNames[i], data.regs[i]);
}
cout << "pc: 0x" << hex << data.regs[32] << dec << endl; // 打印PC寄存器
}
~PerfMap() {
if (fd >= 0) {
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
close(fd);
}
if (mmapAddr != MAP_FAILED) {
munmap(mmapAddr, mmapSize);
}
}
};
// 线程函数
void* monitorThread(void *arg) {
int tid = *static_cast<int*>(arg);
PerfMap perf;
if (perf.create(tid, breakpoint_addr, HW_BREAKPOINT_X, HW_BREAKPOINT_LEN_4)) {
perf.process();
}
delete static_cast<int*>(arg);
return nullptr;
}
int main() {
// 用户输入
char packageName[256];
cout << "Enter package name: ";
cin.getline(packageName, sizeof(packageName));
int pid = getPID(packageName);
if (pid == -1) {
cerr << "Process not found" << endl;
return 1;
}
cout << "PID: " << pid << endl;
// 输入断点地址
char addrStr[32];
cout << "Enter breakpoint address (hex): 0x";
cin.getline(addrStr, sizeof(addrStr));
breakpoint_addr = strtoul(addrStr, NULL, 16);
cout << "Breakpoint at 0x" << hex << breakpoint_addr << dec << endl;
// 获取并监控所有线程
vector<int> tasks = GetProcessTask(pid);
vector<pthread_t> threads;
for (int tid : tasks) {
pthread_t pt;
int *arg = new int(tid); // 线程参数
if (pthread_create(&pt, NULL, monitorThread, arg) == 0) {
threads.push_back(pt);
} else {
cerr << "Failed to create thread for TID: " << tid << endl;
delete arg;
}
}
// 等待退出
cout << "\nMonitoring " << threads.size() << " threads. Press Enter to exit...";
cin.get();
// 清理
for (pthread_t pt : threads) {
pthread_cancel(pt);
pthread_join(pt, NULL);
}
return 0;
}
```
### 关键修复说明
#### 1. `perf_event_open` 参数修复
```cpp
perf_event_attr attr = {};
attr.type = PERF_TYPE_BREAKPOINT;
attr.config = 0; // 关键修复:断点事件不使用此字段[^1]
```
- 原因:原代码设置 `PERF_COUNT_SW_CPU_CLOCK` 与断点类型冲突
- 解决方案:`config` 设置为0(Linux手册要求)
#### 2. 段错误修复
```cpp
mmapAddr = mmap(NULL, mmapSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mmapAddr == MAP_FAILED) { // 添加错误检查
perror("mmap failed");
close(fd);
return false;
}
```
- 添加 `MAP_FAILED` 检查防止空指针访问
- 使用 `lock_guard` 防止多线程输出竞争
#### 3. 内存管理优化
```cpp
~PerfMap() {
if (fd >= 0) {
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
close(fd);
}
if (mmapAddr != MAP_FAILED) {
munmap(mmapAddr, mmapSize);
}
}
```
- 确保资源正确释放
- 使用 RAII 模式管理资源
### 编译指令
```bash
# Android NDK 编译 (arm64-v8a)
$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android31-clang++ \
-std=c++11 -pthread -fPIE -pie \
-o perfmonitor main.cpp
```
### 使用示例
```bash
# 推送到设备
adb push perfmonitor /data/local/tmp
adb shell chmod +x /data/local/tmp/perfmonitor
# 执行监控
adb shell su -c /data/local/tmp/perfmonitor
> Enter package name: com.example.app
> Enter breakpoint address: 0x6fe39f9d80
```
### 性能优化公式
硬件断点性能损耗主要来自上下文切换:
$$T_{total} = N \times (T_{switch} + T_{sample})$$
其中:
- $N$ = 断点触发次数
- $T_{switch}$ ≈ 500ns (上下文切换时间)
- $T_{sample}$ ≈ 200ns (寄存器采样时间)
通过线程过滤和采样率控制,可降低损耗[^3]。