summaryrefslogtreecommitdiff
path: root/source/ucli/ucli-lib.cc
blob: e710d86d70af2127658f4c52b4d4bfb5d4f32ce4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
#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,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;
}