#include #include #include #include #include #include #include #include #include #include #include #include "symbol.h" #include "ucli.h" #include "unwind.h" #define BUF_LEN 4096 #define WHITESPACE " \t\n\r" #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) #define streq(a,b) (strcmp((a),(b)) == 0) unsigned long run_in_host = 0; using namespace std; class pid_cmdline { private: std::map cmdlines; public: void clear(void); std::string &get_pid_cmdline(int pid); }; class pid_cmdline pid_cmdline; static string unknow_symbol("UNKNOWN"); void pid_cmdline::clear(void) { cmdlines.clear(); } std::string &pid_cmdline::get_pid_cmdline(int pid) { if (cmdlines.count(pid) == 0) { int i; char buf[255]; char file[255]; std::fstream ifs; snprintf(file, sizeof(file), "/proc/%d/cmdline", pid); ifs.open(file, ios::binary | ios::in); ifs.getline(buf, 255); for (i = 0; i < ifs.gcount() && i < 255; i++) { if (buf[i] < ' ') { buf[i] = ' '; } } cmdlines[pid] = buf; } return cmdlines[pid]; } /** * @brief 调用ioctl */ long diag_call_ioctl(unsigned long request, unsigned long arg) { long ret = 0; int fd; fd = open(DEVICE, O_RDWR, 0); if (fd < 0) { printf("open %s error,try to open %s\n", DEVICE, DEVICE_BAK); fd = open(DEVICE_BAK, O_RDWR, 0); if (fd < 0) { printf("open %s error!\n", DEVICE_BAK); return EEXIST; } else { printf("open %s success!\n", DEVICE_BAK); } } ret = ioctl(fd, request, arg); if (ret < 0) { printf("call cmd %lx fail, ret is %ld\n", request, ret); goto err; } err: close(fd); return ret; } static int unwind_frame_callback(struct unwind_entry *entry, void *arg) { symbol sym; std::string symbol; // Use std::string instead of string elf_file file; sym.reset(entry->ip); if (g_symbol_parser.find_symbol_in_cache(entry->pid, entry->ip, symbol)) { printf("#~ 0x%lx %s ([symbol])\n", entry->ip, symbol.c_str()); return 0; } if (g_symbol_parser.get_symbol_info(entry->pid, sym, file)) { if (g_symbol_parser.find_elf_symbol(sym, file, entry->pid, entry->pid_ns)) { printf("#~ 0x%lx %s ([symbol])\n", entry->ip, sym.name.c_str()); g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, sym.name); } else { printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[symbol]"); g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol); } } else { printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[vma,elf]"); g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol); } return 0; } void extract_variant_buffer(char *buf, unsigned int len, int (*func)(void *, unsigned int, void *), void *arg) { unsigned int pos = 0; struct diag_variant_buffer_head *head; void *rec; int rec_len; char *ret; char dir[1024] = {0}; ret = getcwd(dir, sizeof(dir)); while (pos < len) { head = (struct diag_variant_buffer_head *)(buf + pos); if (pos + sizeof(struct diag_variant_buffer_head) >= len) break; if (head->magic != DIAG_VARIANT_BUFFER_HEAD_MAGIC_SEALED) break; if (head->len < sizeof(struct diag_variant_buffer_head)) break; rec = (void *)(buf + pos + sizeof(struct diag_variant_buffer_head)); rec_len = head->len - sizeof(struct diag_variant_buffer_head); func(rec, rec_len, arg); pos += head->len; } if (ret) { (void)chdir(dir); } } void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, raw_stack_detail *raw_stack) { struct perf_sample stack_sample; entry_cb_arg_t unwind_arg; static u64 regs_buf[3]; printf(" USER STACK: SP:%lx, BP:%lx, IP:%lx\n", raw_stack->sp, raw_stack->bp, raw_stack->ip); stack_sample.user_stack.offset = 0; stack_sample.user_stack.size = raw_stack->stack_size; stack_sample.user_stack.data = (char *)&raw_stack->stack[0]; stack_sample.user_regs.regs = regs_buf; stack_sample.user_regs.regs[PERF_REG_IP] = raw_stack->ip; stack_sample.user_regs.regs[PERF_REG_SP] = raw_stack->sp; stack_sample.user_regs.regs[PERF_REG_BP] = raw_stack->bp; unwind__get_entries(unwind_frame_callback, &unwind_arg, &g_symbol_parser, pid, ns_pid, &stack_sample); fflush(stdout); } // 根据 sys_task 取值返回对应的字符串 static const char *sys_task_str(int sys_task) { switch (sys_task) { case 0: return "USER_TASK"; case 1: return "SYSTEM_TASK"; case 2: return "IDLE_TASK"; default: return "UNKNOWN_TASK"; } } static const char *user_mode_str(int user_mode) { switch (user_mode) { case 1: return "USER MODE"; case 0: return "SYSTEM MODE"; default: return "UNKNOWN MODE"; } } // 判断 __state 具体是下面哪种状态.输出: 单个状态 | 组合状态 // #define TASK_RUNNING 0x0000 // 正在运行 // #define TASK_INTERRUPTIBLE 0x0001 // 等待事件阻塞 可信号唤醒 // #define TASK_UNINTERRUPTIBLE 0x0002 // 等待事件阻塞 不可信号唤醒 // #define __TASK_STOPPED 0x0004 // 暂停执行 // #define __TASK_TRACED 0x0008 //调试状态 // #define TASK_PARKED 0x0040 // parked 状态,暂停执行 保留在 cpu 但不被调度 // #define TASK_DEAD 0x0080 // dead // #define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) // #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) // #define TASK_IDLE (TASK_UNINTERRUPTIBLE | TASK_NOLOAD) // static const char *state_str(int __state){ // } #include #include #define TASK_RUNNING 0x0000 #define TASK_INTERRUPTIBLE 0x0001 #define TASK_UNINTERRUPTIBLE 0x0002 #define __TASK_STOPPED 0x0004 #define __TASK_TRACED 0x0008 #define TASK_PARKED 0x0040 #define TASK_DEAD 0x0080 #define TASK_WAKEKILL 0x0100 #define TASK_NOLOAD 0x0200 #define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) #define TASK_IDLE (TASK_UNINTERRUPTIBLE | TASK_NOLOAD) std::string state_str(int __state){ std::vector states; if (__state == TASK_RUNNING) states.push_back("TASK_RUNNING"); // if (__state & TASK_RUNNING) states.push_back("TASK_RUNNING"); if (__state & TASK_INTERRUPTIBLE) states.push_back("TASK_INTERRUPTIBLE"); if (__state & TASK_UNINTERRUPTIBLE) states.push_back("TASK_UNINTERRUPTIBLE"); if (__state & __TASK_STOPPED) states.push_back("__TASK_STOPPED"); if (__state & __TASK_TRACED) states.push_back("__TASK_TRACED"); if (__state & TASK_PARKED) states.push_back("TASK_PARKED"); if (__state & TASK_DEAD) states.push_back("TASK_DEAD"); if (__state == TASK_KILLABLE) states.push_back("TASK_KILLABLE"); if (__state == TASK_STOPPED) states.push_back("TASK_STOPPED"); if (__state == TASK_IDLE) states.push_back("TASK_IDLE"); std::string result; for (const auto& state : states) { if (!result.empty()) result += " | "; result += state; } return result; } void printk_task_brief(task_detail *detail) { printf(" TASK INFO: %s [%s / %s], PID: %d / %d\n", sys_task_str(detail->sys_task) ,detail->cgroup_buf, detail->comm, detail->tgid, detail->pid); // printf(" TASK STATE: type: %s, state: %s, state %d\n", sys_task_str(detail->sys_task), state_str(detail->state).c_str(), // detail->state); } void diag_printf_kern_stack(kern_stack_detail *kern_stack) { int i; symbol sym; printf(" KERNEL STACK:\n"); for (i = 0; i < BACKTRACE_DEPTH; i++) { if (kern_stack->stack[i] == (size_t)-1 || kern_stack->stack[i] == 0) { break; } sym.reset(kern_stack->stack[i]); if (g_symbol_parser.find_kernel_symbol(sym)) { printf("#@ 0x%lx %s ([kernel.kallsyms])\n", kern_stack->stack[i], sym.name.c_str()); } else { printf("#@ 0x%lx %s\n", kern_stack->stack[i], "UNKNOWN"); } } } void diag_printf_proc_chains(proc_chains_detail *proc_chains) { int detail = 1; int i; printf(" PROC CHAINS:\n"); for (i = 0; i < PROCESS_CHAINS_COUNT; i++) { if (proc_chains->chains[i][0] == 0) break; if (proc_chains->full_argv[i] == 0 && detail) { string cmdline = pid_cmdline.get_pid_cmdline(proc_chains->tgid[i]); if (cmdline.length() > 0) printf("#^ 0xffffffffffffff %s (UNKNOWN)\n", cmdline.c_str()); else printf("#^ 0xffffffffffffff %s (UNKNOWN)\n", proc_chains->chains[i]); } else { printf("#^ 0xffffffffffffff %s (UNKNOWN)\n", proc_chains->chains[i]); } } } int is_pid_1_has_environ(const char *field) { bool done = false; FILE *f = NULL; int r = 0; size_t l; assert(field); f = fopen("/proc/1/environ", "re"); if (!f) return 0; (void) __fsetlocking(f, FSETLOCKING_BYCALLER); l = strlen(field); do { char line[BUF_LEN]; size_t i; for (i = 0; i < sizeof(line)-1; i++) { int c; c = getc(f); if ((c == EOF)) { done = true; break; } else if (c == 0) break; line[i] = c; } line[i] = 0; if (strneq(line, field, l) && line[l] == '=') { r = 1; goto out; } } while (!done); out: fclose(f); return r; } enum { RUN_IN_HOST = 0, RUN_IN_CONTAINER }; int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { char status[BUF_LEN] = {0}; char *t, *f; size_t len; int r; assert(terminator); assert(filename); assert(pattern); assert(field); int fd = open(filename, O_RDONLY); if (fd < 0) return -errno; r = read(fd, &status, BUF_LEN - 1); if (r < 0) return r; t = status; do { bool pattern_ok; do { t = strstr(t, pattern); if (!t) return -ENOENT; /* Check that pattern occurs in beginning of line. */ pattern_ok = (t == status || t[-1] == '\n'); t += strlen(pattern); } while (!pattern_ok); t += strspn(t, " \t"); if (!*t) return -ENOENT; } while (*t != ':'); t++; if (*t) { t += strspn(t, " \t"); /* Also skip zeros, because when this is used for * capabilities, we don't want the zeros. This way the * same capability set always maps to the same string, * irrespective of the total capability set size. For * other numbers it shouldn't matter. */ t += strspn(t, "0"); /* Back off one char if there's nothing but whitespace and zeros */ if (!*t || isspace(*t)) t--; } len = strcspn(t, terminator); f = strndup(t, len); if (!f) return -ENOMEM; *field = f; return 0; } static int detect_container_by_pid_2(void) { char *s = NULL; int r; r = get_proc_field("/proc/2/status", "PPid", WHITESPACE, &s); if (r >= 0) { if (streq(s, "0")) r = RUN_IN_HOST; else r = RUN_IN_CONTAINER; } else if (r == -ENOENT) r = RUN_IN_CONTAINER; else { printf("Failed to read /proc/2/status: %d\n", r); r = RUN_IN_HOST; } free(s); return r; } int check_in_host(void) { int r; if (is_pid_1_has_environ("container")) r = RUN_IN_CONTAINER; else r = detect_container_by_pid_2(); return r == RUN_IN_HOST; }