diff options
author | Stefani Seibold <stefani@seibold.net> | 2009-09-22 19:45:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:41 -0400 |
commit | d899bf7b55f503ba7d3d07ed27c3a37e270fa7db (patch) | |
tree | 32a5ee7816b2f0cb3261dcca8102b9cafe9251bd | |
parent | cba8aafe1e07dfc8bae5ba78be8e02883bd34d31 (diff) |
procfs: provide stack information for threads
A patch to give a better overview of the userland application stack usage,
especially for embedded linux.
Currently you are only able to dump the main process/thread stack usage
which is showed in /proc/pid/status by the "VmStk" Value. But you get no
information about the consumed stack memory of the the threads.
There is an enhancement in the /proc/<pid>/{task/*,}/*maps and which marks
the vm mapping where the thread stack pointer reside with "[thread stack
xxxxxxxx]". xxxxxxxx is the maximum size of stack. This is a value
information, because libpthread doesn't set the start of the stack to the
top of the mapped area, depending of the pthread usage.
A sample output of /proc/<pid>/task/<tid>/maps looks like:
08048000-08049000 r-xp 00000000 03:00 8312 /opt/z
08049000-0804a000 rw-p 00001000 03:00 8312 /opt/z
0804a000-0806b000 rw-p 00000000 00:00 0 [heap]
a7d12000-a7d13000 ---p 00000000 00:00 0
a7d13000-a7f13000 rw-p 00000000 00:00 0 [thread stack: 001ff4b4]
a7f13000-a7f14000 ---p 00000000 00:00 0
a7f14000-a7f36000 rw-p 00000000 00:00 0
a7f36000-a8069000 r-xp 00000000 03:00 4222 /lib/libc.so.6
a8069000-a806b000 r--p 00133000 03:00 4222 /lib/libc.so.6
a806b000-a806c000 rw-p 00135000 03:00 4222 /lib/libc.so.6
a806c000-a806f000 rw-p 00000000 00:00 0
a806f000-a8083000 r-xp 00000000 03:00 14462 /lib/libpthread.so.0
a8083000-a8084000 r--p 00013000 03:00 14462 /lib/libpthread.so.0
a8084000-a8085000 rw-p 00014000 03:00 14462 /lib/libpthread.so.0
a8085000-a8088000 rw-p 00000000 00:00 0
a8088000-a80a4000 r-xp 00000000 03:00 8317 /lib/ld-linux.so.2
a80a4000-a80a5000 r--p 0001b000 03:00 8317 /lib/ld-linux.so.2
a80a5000-a80a6000 rw-p 0001c000 03:00 8317 /lib/ld-linux.so.2
afaf5000-afb0a000 rw-p 00000000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
Also there is a new entry "stack usage" in /proc/<pid>/{task/*,}/status
which will you give the current stack usage in kb.
A sample output of /proc/self/status looks like:
Name: cat
State: R (running)
Tgid: 507
Pid: 507
.
.
.
CapBnd: fffffffffffffeff
voluntary_ctxt_switches: 0
nonvoluntary_ctxt_switches: 0
Stack usage: 12 kB
I also fixed stack base address in /proc/<pid>/{task/*,}/stat to the base
address of the associated thread stack and not the one of the main
process. This makes more sense.
[akpm@linux-foundation.org: fs/proc/array.c now needs walk_page_range()]
Signed-off-by: Stefani Seibold <stefani@seibold.net>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | Documentation/filesystems/proc.txt | 5 | ||||
-rw-r--r-- | fs/exec.c | 2 | ||||
-rw-r--r-- | fs/proc/array.c | 85 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 19 | ||||
-rw-r--r-- | include/linux/sched.h | 1 | ||||
-rw-r--r-- | kernel/fork.c | 2 | ||||
-rw-r--r-- | mm/Makefile | 4 |
7 files changed, 114 insertions, 4 deletions
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 75988ba26a51..b5aee7838a00 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt | |||
@@ -176,6 +176,7 @@ read the file /proc/PID/status: | |||
176 | CapBnd: ffffffffffffffff | 176 | CapBnd: ffffffffffffffff |
177 | voluntary_ctxt_switches: 0 | 177 | voluntary_ctxt_switches: 0 |
178 | nonvoluntary_ctxt_switches: 1 | 178 | nonvoluntary_ctxt_switches: 1 |
179 | Stack usage: 12 kB | ||
179 | 180 | ||
180 | This shows you nearly the same information you would get if you viewed it with | 181 | This shows you nearly the same information you would get if you viewed it with |
181 | the ps command. In fact, ps uses the proc file system to obtain its | 182 | the ps command. In fact, ps uses the proc file system to obtain its |
@@ -229,6 +230,7 @@ Table 1-2: Contents of the statm files (as of 2.6.30-rc7) | |||
229 | Mems_allowed_list Same as previous, but in "list format" | 230 | Mems_allowed_list Same as previous, but in "list format" |
230 | voluntary_ctxt_switches number of voluntary context switches | 231 | voluntary_ctxt_switches number of voluntary context switches |
231 | nonvoluntary_ctxt_switches number of non voluntary context switches | 232 | nonvoluntary_ctxt_switches number of non voluntary context switches |
233 | Stack usage: stack usage high water mark (round up to page size) | ||
232 | .............................................................................. | 234 | .............................................................................. |
233 | 235 | ||
234 | Table 1-3: Contents of the statm files (as of 2.6.8-rc3) | 236 | Table 1-3: Contents of the statm files (as of 2.6.8-rc3) |
@@ -307,7 +309,7 @@ address perms offset dev inode pathname | |||
307 | 08049000-0804a000 rw-p 00001000 03:00 8312 /opt/test | 309 | 08049000-0804a000 rw-p 00001000 03:00 8312 /opt/test |
308 | 0804a000-0806b000 rw-p 00000000 00:00 0 [heap] | 310 | 0804a000-0806b000 rw-p 00000000 00:00 0 [heap] |
309 | a7cb1000-a7cb2000 ---p 00000000 00:00 0 | 311 | a7cb1000-a7cb2000 ---p 00000000 00:00 0 |
310 | a7cb2000-a7eb2000 rw-p 00000000 00:00 0 | 312 | a7cb2000-a7eb2000 rw-p 00000000 00:00 0 [threadstack:001ff4b4] |
311 | a7eb2000-a7eb3000 ---p 00000000 00:00 0 | 313 | a7eb2000-a7eb3000 ---p 00000000 00:00 0 |
312 | a7eb3000-a7ed5000 rw-p 00000000 00:00 0 | 314 | a7eb3000-a7ed5000 rw-p 00000000 00:00 0 |
313 | a7ed5000-a8008000 r-xp 00000000 03:00 4222 /lib/libc.so.6 | 315 | a7ed5000-a8008000 r-xp 00000000 03:00 4222 /lib/libc.so.6 |
@@ -343,6 +345,7 @@ is not associated with a file: | |||
343 | [stack] = the stack of the main process | 345 | [stack] = the stack of the main process |
344 | [vdso] = the "virtual dynamic shared object", | 346 | [vdso] = the "virtual dynamic shared object", |
345 | the kernel system call handler | 347 | the kernel system call handler |
348 | [threadstack:xxxxxxxx] = the stack of the thread, xxxxxxxx is the stack size | ||
346 | 349 | ||
347 | or if empty, the mapping is anonymous. | 350 | or if empty, the mapping is anonymous. |
348 | 351 | ||
@@ -1357,6 +1357,8 @@ int do_execve(char * filename, | |||
1357 | if (retval < 0) | 1357 | if (retval < 0) |
1358 | goto out; | 1358 | goto out; |
1359 | 1359 | ||
1360 | current->stack_start = current->mm->start_stack; | ||
1361 | |||
1360 | /* execve succeeded */ | 1362 | /* execve succeeded */ |
1361 | current->fs->in_exec = 0; | 1363 | current->fs->in_exec = 0; |
1362 | current->in_execve = 0; | 1364 | current->in_execve = 0; |
diff --git a/fs/proc/array.c b/fs/proc/array.c index 725a650bbbb8..0c6bc602e6c4 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c | |||
@@ -82,6 +82,7 @@ | |||
82 | #include <linux/pid_namespace.h> | 82 | #include <linux/pid_namespace.h> |
83 | #include <linux/ptrace.h> | 83 | #include <linux/ptrace.h> |
84 | #include <linux/tracehook.h> | 84 | #include <linux/tracehook.h> |
85 | #include <linux/swapops.h> | ||
85 | 86 | ||
86 | #include <asm/pgtable.h> | 87 | #include <asm/pgtable.h> |
87 | #include <asm/processor.h> | 88 | #include <asm/processor.h> |
@@ -321,6 +322,87 @@ static inline void task_context_switch_counts(struct seq_file *m, | |||
321 | p->nivcsw); | 322 | p->nivcsw); |
322 | } | 323 | } |
323 | 324 | ||
325 | struct stack_stats { | ||
326 | struct vm_area_struct *vma; | ||
327 | unsigned long startpage; | ||
328 | unsigned long usage; | ||
329 | }; | ||
330 | |||
331 | static int stack_usage_pte_range(pmd_t *pmd, unsigned long addr, | ||
332 | unsigned long end, struct mm_walk *walk) | ||
333 | { | ||
334 | struct stack_stats *ss = walk->private; | ||
335 | struct vm_area_struct *vma = ss->vma; | ||
336 | pte_t *pte, ptent; | ||
337 | spinlock_t *ptl; | ||
338 | int ret = 0; | ||
339 | |||
340 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); | ||
341 | for (; addr != end; pte++, addr += PAGE_SIZE) { | ||
342 | ptent = *pte; | ||
343 | |||
344 | #ifdef CONFIG_STACK_GROWSUP | ||
345 | if (pte_present(ptent) || is_swap_pte(ptent)) | ||
346 | ss->usage = addr - ss->startpage + PAGE_SIZE; | ||
347 | #else | ||
348 | if (pte_present(ptent) || is_swap_pte(ptent)) { | ||
349 | ss->usage = ss->startpage - addr + PAGE_SIZE; | ||
350 | pte++; | ||
351 | ret = 1; | ||
352 | break; | ||
353 | } | ||
354 | #endif | ||
355 | } | ||
356 | pte_unmap_unlock(pte - 1, ptl); | ||
357 | cond_resched(); | ||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | static inline unsigned long get_stack_usage_in_bytes(struct vm_area_struct *vma, | ||
362 | struct task_struct *task) | ||
363 | { | ||
364 | struct stack_stats ss; | ||
365 | struct mm_walk stack_walk = { | ||
366 | .pmd_entry = stack_usage_pte_range, | ||
367 | .mm = vma->vm_mm, | ||
368 | .private = &ss, | ||
369 | }; | ||
370 | |||
371 | if (!vma->vm_mm || is_vm_hugetlb_page(vma)) | ||
372 | return 0; | ||
373 | |||
374 | ss.vma = vma; | ||
375 | ss.startpage = task->stack_start & PAGE_MASK; | ||
376 | ss.usage = 0; | ||
377 | |||
378 | #ifdef CONFIG_STACK_GROWSUP | ||
379 | walk_page_range(KSTK_ESP(task) & PAGE_MASK, vma->vm_end, | ||
380 | &stack_walk); | ||
381 | #else | ||
382 | walk_page_range(vma->vm_start, (KSTK_ESP(task) & PAGE_MASK) + PAGE_SIZE, | ||
383 | &stack_walk); | ||
384 | #endif | ||
385 | return ss.usage; | ||
386 | } | ||
387 | |||
388 | static inline void task_show_stack_usage(struct seq_file *m, | ||
389 | struct task_struct *task) | ||
390 | { | ||
391 | struct vm_area_struct *vma; | ||
392 | struct mm_struct *mm = get_task_mm(task); | ||
393 | |||
394 | if (mm) { | ||
395 | down_read(&mm->mmap_sem); | ||
396 | vma = find_vma(mm, task->stack_start); | ||
397 | if (vma) | ||
398 | seq_printf(m, "Stack usage:\t%lu kB\n", | ||
399 | get_stack_usage_in_bytes(vma, task) >> 10); | ||
400 | |||
401 | up_read(&mm->mmap_sem); | ||
402 | mmput(mm); | ||
403 | } | ||
404 | } | ||
405 | |||
324 | int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, | 406 | int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, |
325 | struct pid *pid, struct task_struct *task) | 407 | struct pid *pid, struct task_struct *task) |
326 | { | 408 | { |
@@ -340,6 +422,7 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, | |||
340 | task_show_regs(m, task); | 422 | task_show_regs(m, task); |
341 | #endif | 423 | #endif |
342 | task_context_switch_counts(m, task); | 424 | task_context_switch_counts(m, task); |
425 | task_show_stack_usage(m, task); | ||
343 | return 0; | 426 | return 0; |
344 | } | 427 | } |
345 | 428 | ||
@@ -481,7 +564,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, | |||
481 | rsslim, | 564 | rsslim, |
482 | mm ? mm->start_code : 0, | 565 | mm ? mm->start_code : 0, |
483 | mm ? mm->end_code : 0, | 566 | mm ? mm->end_code : 0, |
484 | (permitted && mm) ? mm->start_stack : 0, | 567 | (permitted) ? task->stack_start : 0, |
485 | esp, | 568 | esp, |
486 | eip, | 569 | eip, |
487 | /* The signal information here is obsolete. | 570 | /* The signal information here is obsolete. |
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 366b1017a4f1..2a1bef9203c6 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -243,6 +243,25 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) | |||
243 | } else if (vma->vm_start <= mm->start_stack && | 243 | } else if (vma->vm_start <= mm->start_stack && |
244 | vma->vm_end >= mm->start_stack) { | 244 | vma->vm_end >= mm->start_stack) { |
245 | name = "[stack]"; | 245 | name = "[stack]"; |
246 | } else { | ||
247 | unsigned long stack_start; | ||
248 | struct proc_maps_private *pmp; | ||
249 | |||
250 | pmp = m->private; | ||
251 | stack_start = pmp->task->stack_start; | ||
252 | |||
253 | if (vma->vm_start <= stack_start && | ||
254 | vma->vm_end >= stack_start) { | ||
255 | pad_len_spaces(m, len); | ||
256 | seq_printf(m, | ||
257 | "[threadstack:%08lx]", | ||
258 | #ifdef CONFIG_STACK_GROWSUP | ||
259 | vma->vm_end - stack_start | ||
260 | #else | ||
261 | stack_start - vma->vm_start | ||
262 | #endif | ||
263 | ); | ||
264 | } | ||
246 | } | 265 | } |
247 | } else { | 266 | } else { |
248 | name = "[vdso]"; | 267 | name = "[vdso]"; |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 6448bbc6406b..3cbc6c0be666 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1529,6 +1529,7 @@ struct task_struct { | |||
1529 | /* bitmask of trace recursion */ | 1529 | /* bitmask of trace recursion */ |
1530 | unsigned long trace_recursion; | 1530 | unsigned long trace_recursion; |
1531 | #endif /* CONFIG_TRACING */ | 1531 | #endif /* CONFIG_TRACING */ |
1532 | unsigned long stack_start; | ||
1532 | }; | 1533 | }; |
1533 | 1534 | ||
1534 | /* Future-safe accessor for struct task_struct's cpus_allowed. */ | 1535 | /* Future-safe accessor for struct task_struct's cpus_allowed. */ |
diff --git a/kernel/fork.c b/kernel/fork.c index 7cf45812ce84..8f45b0ebdda7 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1095,6 +1095,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1095 | 1095 | ||
1096 | p->bts = NULL; | 1096 | p->bts = NULL; |
1097 | 1097 | ||
1098 | p->stack_start = stack_start; | ||
1099 | |||
1098 | /* Perform scheduler related setup. Assign this task to a CPU. */ | 1100 | /* Perform scheduler related setup. Assign this task to a CPU. */ |
1099 | sched_fork(p, clone_flags); | 1101 | sched_fork(p, clone_flags); |
1100 | 1102 | ||
diff --git a/mm/Makefile b/mm/Makefile index 728a9fde49d1..88193d73cd1a 100644 --- a/mm/Makefile +++ b/mm/Makefile | |||
@@ -11,10 +11,10 @@ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ | |||
11 | maccess.o page_alloc.o page-writeback.o \ | 11 | maccess.o page_alloc.o page-writeback.o \ |
12 | readahead.o swap.o truncate.o vmscan.o shmem.o \ | 12 | readahead.o swap.o truncate.o vmscan.o shmem.o \ |
13 | prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ | 13 | prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ |
14 | page_isolation.o mm_init.o mmu_context.o $(mmu-y) | 14 | page_isolation.o mm_init.o mmu_context.o \ |
15 | pagewalk.o $(mmu-y) | ||
15 | obj-y += init-mm.o | 16 | obj-y += init-mm.o |
16 | 17 | ||
17 | obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o | ||
18 | obj-$(CONFIG_BOUNCE) += bounce.o | 18 | obj-$(CONFIG_BOUNCE) += bounce.o |
19 | obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o | 19 | obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o |
20 | obj-$(CONFIG_HAS_DMA) += dmapool.o | 20 | obj-$(CONFIG_HAS_DMA) += dmapool.o |