烧死10亿脑细胞的SQL长啥样?

1 前言

今天在生产中碰到了一个让我十分费解的 SQL,十分有趣。

2 现象

SQL 很好复现,就是逻辑看起来有点唬人

postgres=# create table test(id1 int,id2 int);
CREATE TABLE
postgres=# insert into test values(1,3),(2,1),(3,1),(3,3);
INSERT 0 4
postgres=# select * from test;
 id1 | id2 
-----+-----
   1 |   3
   2 |   1
   3 |   1
   3 |   3
(4 rows)

业务 SQL 如下👇🏻 此处用 test 表替代,真实情况表中字段存在一个父子关系,根据 parent_id 查找子 id

postgres=# select (exists (select 1 as one from test a where (test.id1 = a.id2))) as b from test;
 b 
---
 t
 f
 t
 t
(4 rows)

postgres=# explain select (exists (select 1 as one from test a where (test.id1 = a.id2))) as b from test;
                          QUERY PLAN                          
--------------------------------------------------------------
 Seq Scan on test  (cost=0.00..3.14 rows=4 width=1)
   SubPlan 2
     ->  Seq Scan on test a  (cost=0.00..1.04 rows=4 width=4)
(3 rows)

SQL 是 self-join ,a 是 test 表的一个别名。

让我们把子查询单独摘出来执行一下

postgres=# select 1 as one from test a where (test.id1 = a.id2);
ERROR:  invalid reference to FROM-clause entry for table "test"
LINE 1: select 1 as one from test a where (test.id1 = a.id2);
                                           ^
HINT:  Perhaps you meant to reference the table alias "a".

可以看到报错了,说明此处的 test 是取自外层的 tes

基地址: 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值