This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-variable-monitor/source/ucli/ucli-lib.cc
2023-12-12 11:55:44 +08:00

466 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}