GNU/Linux - Open函数使用的O_CLOEXEC flag

在 Linux 中,“O_CLOEXEC ”标志与 “open ”系统调用一起使用,用于指定在使用 “exec ”系列函数(如 “execve”、“execl ”等)执行新程序时,“open ”返回的文件描述符应自动关闭。

In Linux, the `O_CLOEXEC` flag is used with the `open` system call to specify that the file descriptor returned by `open` should be automatically closed when executing a new program using one of the `exec` family of functions (such as `execve`, `execl`, etc.).

How it works:

- 文件描述符 程序打开文件时,会获得一个文件描述符 (FD),这是一个代表打开文件的小整数。

- 文件描述符和 `exec`: 默认情况下,当进程调用 `exec` 函数时,进程中打开的文件描述符在新程序中仍保持打开状态。这可能是不可取的,尤其是出于安全原因,因为它可能会无意中将文件描述符泄露给子进程。

- O_CLOEXEC` 标志: 在使用 `open` 系统调用时使用 `O_CLOEXEC` 标志,会为文件描述符设置执行时关闭 (FD_CLOEXEC) 标志。这意味着在执行新程序时,文件描述符将自动关闭。

- File Descriptors: When a program opens a file, it gets a file descriptor (FD), which is a small integer representing the open file.

- File Descriptors and `exec`: By default, when a process calls an `exec` function, the file descriptors that were open in the process remain open in the new program. This can be undesirable, especially for security reasons, as it may inadvertently leak file descriptors to child processes.

- `O_CLOEXEC` Flag: When you use the `O_CLOEXEC` flag with the `open` system call, it sets the close-on-exec (FD_CLOEXEC) flag for the file descriptor. This means that the file descriptor will be automatically closed when a new program is executed.

Example:

int fd = open("example.txt", O_RDONLY | O_CLOEXEC);

if (fd == -1) {

    // handle error

}

在此示例中,如果进程随后调用 `exec` 函数,文件描述符 `fd` 将自动关闭。

In this example, the file descriptor `fd` will be automatically closed if the process later calls an `exec` function.

Why use `O_CLOEXEC`?

- 安全性 防止文件描述符被新程序无意继承,降低敏感信息泄露的风险。

- 资源管理: 确保资源在过渡到新程序时被正确释放。

在需要确保文件描述符不会泄漏到使用 `exec` 创建的子进程中时,使用 `O_CLOEXEC` 是一种常见的最佳做法。

- Security: Prevents file descriptors from being unintentionally inherited by new programs, reducing the risk of leaking sensitive information.

- Resource Management: Ensures that resources are properly released when transitioning to a new program.

Using `O_CLOEXEC` is a common best practice in scenarios where you need to ensure that file descriptors do not leak into child processes created with `exec`.

基地址: 0x765149d000 任务: 31096 任务: 31102 任务: 31103 任务: 31105 任务: 31106 任务: 31107 任务: 31109 任务: 31117 任务: 31120 任务: 31126 任务: 31127 任务: 31131 任务: 31140 任务: 31147 任务: 31148 任务: 31175 任务: 31176 任务: 31178 任务: 31200 任务: 31209 任务: 31224 任务: 31225 任务: 31226 任务: 31230 任务: 31236 任务: 31241 任务: 31242 任务: 31244 任务: 31249 任务: 31250 任务: 31251 任务: 31252 任务: 31255 任务: 31260 任务: 31274 任务: 31275 任务: 31276 任务: 31296 任务: 31297 任务: 31302 任务: 31314 任务: 31321 任务: 31327 任务: 31339 任务: 31364 任务: 31366 任务: 31367 任务: 31370 任务: 31371 任务: 31372 任务: 31373 任务: 31378 任务: 31382 任务: 31383 任务: 31384 任务: 31385 任务: 31387 任务: 31388 任务: 31389 任务: 31390 任务: 31403 任务: 31405 任务: 31406 任务: 31407 任务: 31408 任务: 31409 任务: 31418 任务: 31419 任务: 31421 任务: 31432 任务: 31434 任务: 31435 任务: 31436 任务: 31440 任务: 31441 任务: 31442 任务: 31443 任务: 31444 任务: 31445 任务: 31446 任务: 31451 任务: 31461 任务: 31467 任务: 31468 任务: 31469 任务: 31471 任务: 31472 任务: 31547 任务: 31548 任务: 31578 任务: 31581 任务: 31585 任务: 31586 任务: 31587 任务: 31591 任务: 31592 任务: 31594 任务: 31595 任务: 31596 任务: 31601 任务: 31602 任务: 31604 任务: 31605 任务: 31606 任务: 31607 任务: 31608 任务: 31609 任务: 31611 任务: 31613 任务: 31618 任务: 31621 任务: 31625 任务: 31626 任务: 31627 任务: 31628 任务: 31629 任务: 31635 任务: 31692 任务: 31693 任务: 31712 任务: 31713 任务: 31714 任务: 31730 任务: 31731 任务: 31732 任务: 31733 任务: 31734 任务: 31761 任务: 31763 任务: 31764 任务: 31765 任务: 31766 任务: 31767 任务: 31774 任务: 31780 任务: 31800 任务: 31801 任务: 31802 任务: 31822 任务: 31824 任务: 31831 任务: 31859 任务: 31860 任务: 31862 任务: 31863 任务: 31864 任务: 31868 任务: 31873 任务: 31875 任务: 31876 任务: 31879 任务: 31884 任务: 31890 任务: 31892 任务: 31894 任务: 31895 任务: 31897 任务: 31900 任务: 31903 任务: 31917 任务: 31925 任务: 31930 任务: 31944 任务: 31959 任务: 31969 任务: 31973 任务: 31976 任务: 31981 任务: 32049 任务: 32051 任务: 32063 任务: 32064 任务: 32102 任务: 32107 任务: 32108 任务: 32109 任务: 32110 任务: 32158 任务: 32161 任务: 32206 任务: 32208 任务: 32209 任务: 32218 任务: 32219 任务: 32220 任务: 32223 任务: 32224 任务: 32226 任务: 32228 任务: 32230 任务: 32231 任务: 32233 任务: 32235 任务: 32239 任务: 32240 任务: 32247 任务: 32253 任务: 32256 任务: 32273 任务: 32327 任务: 32329 任务: 32330 任务: 32333 任务: 32347 任务: 32425 任务: 32450 任务: 32451 perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument perf_event_open error: Invalid argument 监控已启动,按任意键退出... perf_event_open error: Invalid argument perf_event_open error: Invalid argument Segmentation fault 进行断点后出现问题 怎么解决。帮我修复好完整发给我
最新发布
07-14
<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]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值