466 lines
11 KiB
C++
466 lines
11 KiB
C++
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <fstream>
|
||
#include <stdio.h>
|
||
#include <sys/ioctl.h>
|
||
#include <unistd.h>
|
||
|
||
#include <assert.h>
|
||
#include <ctype.h>
|
||
#include <stdio_ext.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#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<int, std::string> 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;
|
||
}
|
||
// printf("#~ sym1 0x%lx\n", sym.ip);
|
||
if (g_symbol_parser.get_symbol_info(entry->pid, sym, file)) {
|
||
// printf("#~ sym2 0x%lx\n", sym.ip);
|
||
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 <string>
|
||
#include <vector>
|
||
|
||
#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<std::string> 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;
|
||
}
|