-
Notifications
You must be signed in to change notification settings - Fork 0
/
task.c
343 lines (292 loc) · 9.15 KB
/
task.c
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
#include "common.h"
#include "kcore.h"
#include "kernel.h"
#include "mm.h"
#include "error.h"
#define TASK_SLUSH (20)
#define MAX_UNLIMITED_TASK_RETRIES (500)
struct task_table task_table = { 0 };
struct task_table *tt = &task_table;
extern char *exec_cmd_return_string(char *cmd);
extern long exec_cmd_return_long(char *cmd, int base);
extern unsigned long exec_cmd_return_ulong(char *cmd, int base);
/*
* Allocate or re-allocated space for the task_context array and task list.
*/
static void
allocate_task_space(int cnt)
{
if (tt->context_array == NULL) {
if (!(tt->task_local = (void *)malloc(cnt * sizeof(void *))))
fprintf(stderr, "cannot malloc kernel task array (%d tasks)", cnt);
if (!(tt->context_array = (struct task_context *)malloc(cnt * sizeof(struct task_context))))
printf(stderr, "cannot malloc context array (%d tasks)", cnt);
if (!(tt->context_by_task = (struct task_context **)malloc(cnt * sizeof(struct task_context*))))
fprintf(stderr, "cannot malloc context_by_task array (%d tasks)",
cnt);
/*
* if (!(tt->tgid_array = (struct tgid_context *)malloc(cnt * sizeof(struct tgid_context))))
* fprintf("cannot malloc tgid array (%d tasks)",
* cnt);
*/
} else {
if (!(tt->task_local = (void *)realloc(tt->task_local, cnt * sizeof(void *))))
fprintf(stderr, "cannot realloc kernel task array (%d tasks)", cnt);
if (!(tt->context_array = (struct task_context *)realloc(tt->context_array,
cnt * sizeof(struct task_context))))
fprintf(stderr, "cannot realloc context array (%d tasks)", cnt);
if (!(tt->context_by_task = (struct task_context **)realloc(tt->context_by_task,
cnt * sizeof(struct task_context*))))
fprintf(stderr, "cannot realloc context_by_task array (%d tasks)", cnt);
/*
* if (!(tt->tgid_array = (struct tgid_context *)realloc(tt->tgid_array,
* cnt * sizeof(struct tgid_context))))
* fprintf(stderr, "%scannot realloc tgid array (%d tasks)",
* (pc->flags & RUNTIME) ? "" : "\n", cnt);
*/
}
}
char * fill_task_struct(unsigned long task)
{
if (tt->task_struct == NULL)
tt->task_struct = (char *)malloc(ASSIGN_SIZE(task_struct));
if (!readmem(task, KVADDR, tt->task_struct, ASSIGN_SIZE(task_struct), "fill_task_struct",
RETURN_ON_ERROR)) {
tt->last_task_read = 0;
return NULL;
}
tt->last_task_read = task;
return tt->task_struct;
}
/*
* Linux 4.20: pid_hash[] IDR changed from radix tree to xarray
*/
static int xarray_task_callback(unsigned long task)
{
unsigned long *tlp;
if (tt->callbacks < tt->max_tasks) {
tlp = (unsigned long *)tt->task_local;
tlp += tt->callbacks++;
*tlp = task;
}
return TRUE;
}
static char * lookup_task_xarray_task_table(pid_t target)
{
int i, cnt;
unsigned long count, retries, next, curtask, curpid, upid_ns, pid_tasks_0, task_addr;
unsigned long *tlp;
char *tp;
struct list_pair xp;
char *pidbuf;
pid_t scan_pid;
curpid = NO_PID;
curtask = NO_TASK;
count = do_xarray(pid_xarray, XARRAY_COUNT, NULL);
if (1)
printf("xarray: count: %ld\n", count);
retries = 0;
pidbuf = (char *)malloc(ASSIGN_SIZE(pid));
retry_xarray:
if (retries)
printf("\ncannot gather a stable task list via xarray\n");
if (retries == MAX_UNLIMITED_TASK_RETRIES) {
printf("\ncannot gather a stable task list via xarray (%d retries)\n",
retries);
exit(-1);
}
if (count > tt->max_tasks) {
tt->max_tasks = count + TASK_SLUSH;
allocate_task_space(tt->max_tasks);
}
memset(tt->task_local, '\0', count * sizeof(void *));
tt->callbacks = 0;
xp.index = 0;
xp.value = (void *)&xarray_task_callback;
count = do_xarray(pid_xarray, XARRAY_DUMP_CB, &xp);
if (kr_debug)
printf("do_xarray: count: %ld tt->callbacks: %d\n", count, tt->callbacks);
if (count > tt->max_tasks) {
retries++;
goto retry_xarray;
}
for (i = 0; i < count; i++) {
tlp = (unsigned long *)tt->task_local;
tlp += i;
if ((next = *tlp) == 0)
break;
/*
* Translate xarray contents to PIDTYPE_PID task.
* - the xarray contents are struct pid pointers
* - upid is contained in pid.numbers[0]
* - upid.ns should point to init->init_pid_ns
* - pid->tasks[0] is first hlist_node in task->pids[3]
* - get task from address of task->pids[0]
*/
if (!readmem(next, KVADDR, pidbuf, ASSIGN_SIZE(pid),
"pid", RETURN_ON_ERROR|QUIET)) {
printf("\ncannot read pid struct from xarray\n");
retries++;
goto retry_xarray;
}
upid_ns = ULONG(pidbuf + OFFSET(pid_numbers) + OFFSET(upid_ns));
if (upid_ns != tt->init_pid_ns)
continue;
pid_tasks_0 = ULONG(pidbuf + OFFSET(pid_tasks));
if (!pid_tasks_0)
continue;
/*
* FIXME
* pids in task_struct is NOT valid member.
*/
if (VALID_MEMBER(task_struct_pids))
task_addr = pid_tasks_0 - OFFSET(task_struct_pids);
else
task_addr = pid_tasks_0 - OFFSET(task_struct_pid_links);
if (kr_debug)
printf("pid: %lx ns: %lx tasks[0]: %lx task: %lx\n",
next, upid_ns, pid_tasks_0, task_addr);
readmem(task_addr + OFFSET(task_struct_pid), KVADDR, &scan_pid,
sizeof(pid_t), "pid", RETURN_ON_ERROR|QUIET);
if (!IS_TASK_ADDR(task_addr)) {
printf("IDR xarray: invalid task address: %lx\n", task_addr);
retries++;
goto retry_xarray;
}
cnt++;
if (target != -1 && scan_pid == target)
goto find;
}
if (!task_addr) {
return NULL;
}
find:
free(pidbuf);
tt->retries = MAX(tt->retries, retries);
return fill_task_struct(task_addr);
}
void dump_task(pid_t pid)
{
char *task = NULL;
int pid_max;
char buf[] = "cat /proc/sys/kernel/pid_max";
char comm[TASK_COMM_LEN];
MEMBER_OFFSET_INIT(task_struct_comm, "task_struct", "comm");
MEMBER_OFFSET_INIT(task_struct_pid, "task_struct", "pid");
pid_max = exec_cmd_return_long(buf, 0);
if (pid > pid_max) {
fprintf(stderr, "pid: %d beyond pid_max (%d)", pid, pid_max);
return;
}
/* task is a physical address */
task = lookup_task_xarray_task_table(pid);
if (!task) {
ERROR("Not find task");
exit(-1);
return;
}
/* dump comm pid */
printf("comm: %s\n", task + OFFSET(task_struct_comm));
}
void *init_vma(pid_t pid, int *nr_vma)
{
int maps_lines;
char buf[100]={0};
struct vma *vma_array;
sprintf(buf, "cat /proc/%d/maps | awk \'END{print NR}\'", pid);
maps_lines = exec_cmd_return_long(buf, 0);
*nr_vma = maps_lines;
vma_array = (struct vma*)malloc(maps_lines * sizeof(struct vma));
for (int i=0; i<maps_lines; i++) {
memset(buf, '\0', 100);
sprintf(buf, "cat /proc/%d/maps | awk \'NR==%d\' | tr \'-\' \' \' | awk \'{print $1}\'", pid, i+1, pid);
vma_array[i].start_addr = exec_cmd_return_ulong(buf, 16);
memset(buf, '\0', 100);
sprintf(buf, "cat /proc/%d/maps | awk \'NR==%d\' | tr \'-\' \' \' | awk \'{print $2}\'", pid, i+1, pid);
vma_array[i].end_addr = exec_cmd_return_ulong(buf, 16);
memset(buf, '\0', 100);
sprintf(buf, "cat /proc/%d/maps | awk \'NR==%d\' | awk \'{print $2}\'", pid, i+1, pid);
vma_array[i].prot = exec_cmd_return_string(buf);
}
return vma_array;
}
#define NODES_SHIFT 3
#define NODES_WIDTH NODES_SHIFT
#define NODES_MASK ((1UL << NODES_WIDTH) - 1)
#define SECTIONS_WIDTH 0
#define SECTIONS_PGOFF ((sizeof(unsigned long)*8) - SECTIONS_WIDTH)
#define NODES_PGOFF (SECTIONS_PGOFF - NODES_WIDTH)
#define NODES_PGSHIFT (NODES_PGOFF * (NODES_WIDTH != 0))
/*
* This function is mainly to convert page_flags into
* node number.
*
* In most time, the kernel config will affect this
* function. The below function depend on:
* 1. CONFIG_SPARSEMEM=y
* 2. CONFIG_VMEMMAP=y
* 3. CONFIG_NODES_SHIFT=3
*/
static inline int page_to_nid(long page_flags)
{
return (page_flags >> NODES_PGSHIFT) & NODES_MASK;
}
/*
* Show the distribution of pgtable.
*
* static inline int page_to_nid(const struct page *page)
* {
* struct page *p = (struct page *)page;
*
* return (PF_POISONED_CHECK(p)->flags >> NODES_PGSHIFT) & NODES_MASK;
* }
*
* Consider the difference of data, such as text, data and DSOs pgtable.
* And the different data maybe have various of affect. So, it is necessary
* to stat each of pgtable separately.
*/
void stat_pgtable(pid_t pid)
{
struct vma *vma;
struct task_context target_context;
struct node_stat pgtable_stat[4];
unsigned long flags;
int nr_vma = 0;
STRUCT_SIZE_INIT(page, "page");
MEMBER_OFFSET_INIT(page_flags, "page", "flags");
target_context.mm_struct = ULONG(tt->task_struct + OFFSET(task_struct_mm));
vma = init_vma(pid, &nr_vma);
unsigned long sz = PAGE_SIZE;
memset(pgtable_stat, 0, sizeof(pgtable_stat));
for(int i=0; i<nr_vma; i++) {
for (unsigned long addr=vma[i].start_addr; addr<vma[i].end_addr; addr += sz) {
arm64_get_pgtable(&target_context, addr, &flags, &sz, 0);
if (page_to_nid(flags) >= 4) {
printf("error, the nid over 4\n");
goto out;
}
pgtable_stat[page_to_nid(flags)].nr++;
}
}
/*
* FIXME: The number of node is required from system, not set 4
* directly.
*/
printf("leaf pgtable stat:\n");
for (int i=0; i<4; i++) {
printf("node %d: %d\n", i, pgtable_stat[i].nr);
}
out:
return;
}
void dump_pte(pid_t pid, unsigned long uvaddr)
{
unsigned long mm, paddr;
struct task_context target_context;
if (tt->last_task_read == 0) {
ERROR("dump pte failed, task address is not setting");
}
target_context.mm_struct = ULONG(tt->task_struct + OFFSET(task_struct_mm));
uvtop(&target_context, uvaddr, &paddr, 1);
return;
}