From eaed7462890cc1886f7b542aa861bc11f8217df3 Mon Sep 17 00:00:00 2001 From: Robin Hack Date: Fri, 28 Mar 2014 11:52:10 -0400 Subject: [PATCH] PR16665: new tapset for task resource limit queries + demo script * tapset/linux/rlimit.stp, task.stp: New function task_rlimit() and assistants. * testsuite/systemtap.examples/process/rlimit_nofile.*: New sample. --- doc/SystemTap_Tapset_Reference/tapsets.tmpl | 9 ++ tapset/linux/rlimit.stp | 55 ++++++++ tapset/linux/task.stp | 128 ++++++++++++++++++ testsuite/buildok/task_rlimit_test.stp | 20 +++ .../process/rlimit_nofile.meta | 7 + .../process/rlimit_nofile.stp | 38 ++++++ 6 files changed, 257 insertions(+) create mode 100644 tapset/linux/rlimit.stp create mode 100755 testsuite/buildok/task_rlimit_test.stp create mode 100644 testsuite/systemtap.examples/process/rlimit_nofile.meta create mode 100755 testsuite/systemtap.examples/process/rlimit_nofile.stp diff --git a/doc/SystemTap_Tapset_Reference/tapsets.tmpl b/doc/SystemTap_Tapset_Reference/tapsets.tmpl index 27b16909e..ca65bb44d 100644 --- a/doc/SystemTap_Tapset_Reference/tapsets.tmpl +++ b/doc/SystemTap_Tapset_Reference/tapsets.tmpl @@ -299,6 +299,15 @@ It contains the following functions: !Itapset/errno.stp + + + RLIMIT Tapset + + This set of functions is used to handle string which defines resource limits (RLIMIT_*) and returns + corresponding number of resource limit. + It contains the following functions: + +!Itapset/rlimit.stp Device Tapset diff --git a/tapset/linux/rlimit.stp b/tapset/linux/rlimit.stp new file mode 100644 index 000000000..10b38b2a9 --- /dev/null +++ b/tapset/linux/rlimit.stp @@ -0,0 +1,55 @@ +%{ +#include +%} + +/** + * sfunction rlimit_from_str - Symbolic string associated with resource limit code + * + * @lim_str: The string representation of limit + * + * Description: This function returns the number associated + * with the given string, such as 0 for the string RLIMIT_CPU, or + * -1 for an out-of-range value. + */ +function rlimit_from_str:long (lim_str:string) +%{ /* pure */ + char *lim_str = (char *)(long)STAP_ARG_lim_str; + +#define aux_rlimit(limit_arg) \ + if (strncmp(lim_str, #limit_arg, MAXSTRINGLEN) == 0) { \ + STAP_RETVALUE = limit_arg; \ + return; \ + } + + /* Little kernel history digging */ + /* This set is stable from 2.6.1 kernel version */ + aux_rlimit(RLIMIT_CPU); + aux_rlimit(RLIMIT_FSIZE); + aux_rlimit(RLIMIT_DATA); + aux_rlimit(RLIMIT_STACK); + aux_rlimit(RLIMIT_CORE); + aux_rlimit(RLIMIT_RSS); + aux_rlimit(RLIMIT_NPROC); + aux_rlimit(RLIMIT_NOFILE); + aux_rlimit(RLIMIT_MEMLOCK); + aux_rlimit(RLIMIT_AS); + aux_rlimit(RLIMIT_LOCKS); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8) + aux_rlimit(RLIMIT_SIGPENDING); + aux_rlimit(RLIMIT_MSGQUEUE); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) + aux_rlimit(RLIMIT_NICE); + aux_rlimit(RLIMIT_RTPRIO); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) + aux_rlimit(RLIMIT_RTTIME); +#endif + + STAP_RETVALUE = -1; + +#undef aux_rlimit +%} diff --git a/tapset/linux/task.stp b/tapset/linux/task.stp index be8598718..f317e2755 100644 --- a/tapset/linux/task.stp +++ b/tapset/linux/task.stp @@ -32,6 +32,134 @@ function task_current:long () %{ /* pure */ STAP_RETVALUE = (long)current; %} +function _task_rlimit_cur:long (task:long, nd_limit:long) +{ + if (nd_limit < 0 || nd_limit >= %{ RLIM_NLIMITS %}) { + return -1; + } + sig = @cast(task, "task_struct", "kernel")->signal; + rlimit = @cast(sig, "signal_struct", "kernel")->rlim; + return @cast(rlimit, "rlimit", "kernel")[nd_limit]->rlim_cur; +} + +/* sfunction task_rlimit - The current resource limit of the tash + * + * @task: task_struct pointer + * @lim_str: String representing limit. + * + * Description: Little bit slower way how ger resource limits of + * process. + * There is need translate string into number for each call. + */ +function task_rlimit:long (task:long, lim_str:string) +{ + lim = rlimit_from_str(lim_str); + if (lim == -1) { return -1; } + return _task_rlimit_cur(task, lim); +} + +/* Fast and "safe" way how to do it. */ + +function task_rlimit_cpu:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_CPU %} ); +} + +function task_rlimit_fsize:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_FSIZE %}); +} + +function task_rlimit_data:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_DATA %}); +} + +function task_rlimit_stack:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_STACK %}); +} + +function task_rlimit_core:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_CORE %}); +} + +function task_rlimit_rss:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_RSS %}); +} + +function task_rlimit_nproc:long (task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_NPROC %}); +} + +function task_rlimit_nofile:long(task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_NOFILE %}); +} + +function task_rlimit_memlock:long(task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_MEMLOCK %}); +} + +function task_rlimit_as:long(task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_AS %}); +} + +function task_rlimit_locks:long(task:long) +{ + return _task_rlimit_cur(task, %{ RLIMIT_LOCKS %}); +} + +function task_rlimit_sigpending:long(task:long) +{ + %( kernel_v >= "2.6.8" %? + return _task_rlimit_cur(task, %{ RLIMIT_SIGPENDING %}); + %: + return -1 + %) +} + +function task_rlimit_msgqueue:long(task:long) +{ + %( kernel_v >= "2.6.8" %? + return _task_rlimit_cur(task, %{ RLIMIT_MSGQUEUE %}); + %: + return -1 + %) +} + +function task_rlimit_nice:long(task:long) +{ + %( kernel_v >= "2.6.12" %? + return _task_rlimit_cur(task, %{ RLIMIT_NICE %}); + %: + return -1 + %) +} + +function task_rlimit_rtprio:long(task:long) +{ + %( kernel_v >= "2.6.12" %? + return _task_rlimit_cur(task, %{ RLIMIT_RTPRIO %}); + %: + return -1 + %) +} + +function task_rlimit_rttime:long(task:long) +{ + %( kernel_v >= "2.6.25" %? + return _task_rlimit_cur(task, %{ RLIMIT_RTTIME %}); + %: + return -1 + %) +} + /** * sfunction task_parent - The task_struct of the parent task * diff --git a/testsuite/buildok/task_rlimit_test.stp b/testsuite/buildok/task_rlimit_test.stp new file mode 100755 index 000000000..df48e5406 --- /dev/null +++ b/testsuite/buildok/task_rlimit_test.stp @@ -0,0 +1,20 @@ +probe begin +{ + print (task_rlimit_cpu(0) + + task_rlimit_fsize(0) + + task_rlimit_data(0) + + task_rlimit_stack(0) + + task_rlimit_core(0) + + task_rlimit_rss(0) + + task_rlimit_nproc(0) + + task_rlimit_nofile(0) + + task_rlimit_memlock(0) + + task_rlimit_as(0) + + task_rlimit_locks(0) + + task_rlimit_sigpending(0) + + task_rlimit_msgqueue(0) + + task_rlimit_nice(0) + + task_rlimit_rtprio(0) + + task_rlimit_rttime(0) + + rlimit_from_str("")); +} diff --git a/testsuite/systemtap.examples/process/rlimit_nofile.meta b/testsuite/systemtap.examples/process/rlimit_nofile.meta new file mode 100644 index 000000000..cb20ee57a --- /dev/null +++ b/testsuite/systemtap.examples/process/rlimit_nofile.meta @@ -0,0 +1,7 @@ +title: Trace processes running out of file descriptors +name: rlimit_nofile.stp +keywords: limits +subsystem: process +description: This script watches processes being scheduled and which try to allocate a file descriptor without luck. +test_check: stap -p4 rlimit_nofile.stp +test_installcheck: stap rlimit_nofile.stp -c "sleep 0.2" diff --git a/testsuite/systemtap.examples/process/rlimit_nofile.stp b/testsuite/systemtap.examples/process/rlimit_nofile.stp new file mode 100755 index 000000000..3a18d5dd8 --- /dev/null +++ b/testsuite/systemtap.examples/process/rlimit_nofile.stp @@ -0,0 +1,38 @@ +############################################################ +# rlimit_nofile.stp +# Author: Robin Hack +# This script watches processes being scheduled and which +# try to allocate a file descriptor without luck. +############################################################ + +global failed_calls + +probe kernel.trace("sched_wakeup") +{ + pid = task_pid($p); + name = task_execname($p); + open_ds = task_open_file_handles($p); + max_ds = task_rlimit_nofile($p); + if (failed_calls[name, pid] == "EMFILE") { + printf ("%s %s(%d) open: %d - max: %d Hit: %s\n", + ctime(gettimeofday_s()), name, pid, open_ds, max_ds, + failed_calls[name, pid]); + } +} + +probe kernel.trace("sched_process_exit") +{ + pid = task_pid($p); + name = task_execname($p); + delete failed_calls[name, pid]; +} + +# This is exactly point where all the fun happens +# This function returns only EMFILE errno. +probe kernel.function("__alloc_fd@fs/file.c").return +{ + if (errno_str($return) == "EMFILE") + { + failed_calls[execname(), pid()] = errno_str($return); + } +} -- 2.43.5