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-04 23:03:19 -05:00

365 lines
8.1 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 <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fstream>
#include <assert.h>
#include <stdio_ext.h>
#include <ctype.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!\n", DEVICE);
return -EEXIST;
}
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(" 用户态堆栈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);
}
void printk_task_brief(task_detail *detail) {
printf(" 进程信息: [%s / %s] PID %d / %d\n", detail->cgroup_buf,
detail->comm, detail->tgid, detail->pid);
}
void diag_printf_kern_stack(kern_stack_detail *kern_stack) {
int i;
symbol sym;
printf(" 内核态堆栈:\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(" 进程链信息:\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;
}