diff options
| author | Maxim Uvarov <muvarov@ru.mvista.com> | 2007-07-16 02:40:48 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:46 -0400 |
| commit | b663a79c191508f27cd885224b592a878c0ba0f6 (patch) | |
| tree | 270a0472bf1fb0c7323ecb60ec68e40d02dcb1b4 | |
| parent | a6c15c2b0fbfd5c0a84f5f0e1e3f20f85d2b8692 (diff) | |
taskstats: add context-switch counters
Make available to the user the following task and process performance
statistics:
* Involuntary Context Switches (task_struct->nivcsw)
* Voluntary Context Switches (task_struct->nvcsw)
Statistics information is available from:
1. taskstats interface (Documentation/accounting/)
2. /proc/PID/status (task only).
This data is useful for detecting hyperactivity patterns between processes.
[akpm@linux-foundation.org: cleanup]
Signed-off-by: Maxim Uvarov <muvarov@ru.mvista.com>
Cc: Shailabh Nagar <nagar@watson.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Jay Lan <jlan@engr.sgi.com>
Cc: Jonathan Lim <jlim@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | Documentation/accounting/getdelays.c | 19 | ||||
| -rw-r--r-- | Documentation/accounting/taskstats-struct.txt | 6 | ||||
| -rw-r--r-- | fs/proc/array.c | 10 | ||||
| -rw-r--r-- | include/linux/taskstats.h | 5 | ||||
| -rw-r--r-- | kernel/taskstats.c | 4 |
5 files changed, 41 insertions, 3 deletions
diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c index 71acc28ed0d1..24c5aade8998 100644 --- a/Documentation/accounting/getdelays.c +++ b/Documentation/accounting/getdelays.c | |||
| @@ -49,6 +49,7 @@ char name[100]; | |||
| 49 | int dbg; | 49 | int dbg; |
| 50 | int print_delays; | 50 | int print_delays; |
| 51 | int print_io_accounting; | 51 | int print_io_accounting; |
| 52 | int print_task_context_switch_counts; | ||
| 52 | __u64 stime, utime; | 53 | __u64 stime, utime; |
| 53 | 54 | ||
| 54 | #define PRINTF(fmt, arg...) { \ | 55 | #define PRINTF(fmt, arg...) { \ |
| @@ -195,7 +196,7 @@ void print_delayacct(struct taskstats *t) | |||
| 195 | "IO %15s%15s\n" | 196 | "IO %15s%15s\n" |
| 196 | " %15llu%15llu\n" | 197 | " %15llu%15llu\n" |
| 197 | "MEM %15s%15s\n" | 198 | "MEM %15s%15s\n" |
| 198 | " %15llu%15llu\n\n", | 199 | " %15llu%15llu\n" |
| 199 | "count", "real total", "virtual total", "delay total", | 200 | "count", "real total", "virtual total", "delay total", |
| 200 | t->cpu_count, t->cpu_run_real_total, t->cpu_run_virtual_total, | 201 | t->cpu_count, t->cpu_run_real_total, t->cpu_run_virtual_total, |
| 201 | t->cpu_delay_total, | 202 | t->cpu_delay_total, |
| @@ -204,6 +205,14 @@ void print_delayacct(struct taskstats *t) | |||
| 204 | "count", "delay total", t->swapin_count, t->swapin_delay_total); | 205 | "count", "delay total", t->swapin_count, t->swapin_delay_total); |
| 205 | } | 206 | } |
| 206 | 207 | ||
| 208 | void task_context_switch_counts(struct taskstats *t) | ||
| 209 | { | ||
| 210 | printf("\n\nTask %15s%15s\n" | ||
| 211 | " %15lu%15lu\n", | ||
| 212 | "voluntary", "nonvoluntary", | ||
| 213 | t->nvcsw, t->nivcsw); | ||
| 214 | } | ||
| 215 | |||
| 207 | void print_ioacct(struct taskstats *t) | 216 | void print_ioacct(struct taskstats *t) |
| 208 | { | 217 | { |
| 209 | printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n", | 218 | printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n", |
| @@ -235,7 +244,7 @@ int main(int argc, char *argv[]) | |||
| 235 | struct msgtemplate msg; | 244 | struct msgtemplate msg; |
| 236 | 245 | ||
| 237 | while (1) { | 246 | while (1) { |
| 238 | c = getopt(argc, argv, "diw:r:m:t:p:vl"); | 247 | c = getopt(argc, argv, "qdiw:r:m:t:p:vl"); |
| 239 | if (c < 0) | 248 | if (c < 0) |
| 240 | break; | 249 | break; |
| 241 | 250 | ||
| @@ -248,6 +257,10 @@ int main(int argc, char *argv[]) | |||
| 248 | printf("printing IO accounting\n"); | 257 | printf("printing IO accounting\n"); |
| 249 | print_io_accounting = 1; | 258 | print_io_accounting = 1; |
| 250 | break; | 259 | break; |
| 260 | case 'q': | ||
| 261 | printf("printing task/process context switch rates\n"); | ||
| 262 | print_task_context_switch_counts = 1; | ||
| 263 | break; | ||
| 251 | case 'w': | 264 | case 'w': |
| 252 | logfile = strdup(optarg); | 265 | logfile = strdup(optarg); |
| 253 | printf("write to file %s\n", logfile); | 266 | printf("write to file %s\n", logfile); |
| @@ -389,6 +402,8 @@ int main(int argc, char *argv[]) | |||
| 389 | print_delayacct((struct taskstats *) NLA_DATA(na)); | 402 | print_delayacct((struct taskstats *) NLA_DATA(na)); |
| 390 | if (print_io_accounting) | 403 | if (print_io_accounting) |
| 391 | print_ioacct((struct taskstats *) NLA_DATA(na)); | 404 | print_ioacct((struct taskstats *) NLA_DATA(na)); |
| 405 | if (print_task_context_switch_counts) | ||
| 406 | task_context_switch_counts((struct taskstats *) NLA_DATA(na)); | ||
| 392 | if (fd) { | 407 | if (fd) { |
| 393 | if (write(fd, NLA_DATA(na), na->nla_len) < 0) { | 408 | if (write(fd, NLA_DATA(na), na->nla_len) < 0) { |
| 394 | err(1,"write error\n"); | 409 | err(1,"write error\n"); |
diff --git a/Documentation/accounting/taskstats-struct.txt b/Documentation/accounting/taskstats-struct.txt index 661c797eaf79..8aa7529f8258 100644 --- a/Documentation/accounting/taskstats-struct.txt +++ b/Documentation/accounting/taskstats-struct.txt | |||
| @@ -22,6 +22,8 @@ There are three different groups of fields in the struct taskstats: | |||
| 22 | /* Extended accounting fields end */ | 22 | /* Extended accounting fields end */ |
| 23 | Their values are collected if CONFIG_TASK_XACCT is set. | 23 | Their values are collected if CONFIG_TASK_XACCT is set. |
| 24 | 24 | ||
| 25 | 4) Per-task and per-thread context switch count statistics | ||
| 26 | |||
| 25 | Future extension should add fields to the end of the taskstats struct, and | 27 | Future extension should add fields to the end of the taskstats struct, and |
| 26 | should not change the relative position of each field within the struct. | 28 | should not change the relative position of each field within the struct. |
| 27 | 29 | ||
| @@ -158,4 +160,8 @@ struct taskstats { | |||
| 158 | 160 | ||
| 159 | /* Extended accounting fields end */ | 161 | /* Extended accounting fields end */ |
| 160 | 162 | ||
| 163 | 4) Per-task and per-thread statistics | ||
| 164 | __u64 nvcsw; /* Context voluntary switch counter */ | ||
| 165 | __u64 nivcsw; /* Context involuntary switch counter */ | ||
| 166 | |||
| 161 | } | 167 | } |
diff --git a/fs/proc/array.c b/fs/proc/array.c index 680c913575f0..9cbab7e93557 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c | |||
| @@ -289,6 +289,15 @@ static inline char *task_cap(struct task_struct *p, char *buffer) | |||
| 289 | cap_t(p->cap_effective)); | 289 | cap_t(p->cap_effective)); |
| 290 | } | 290 | } |
| 291 | 291 | ||
| 292 | static inline char *task_context_switch_counts(struct task_struct *p, | ||
| 293 | char *buffer) | ||
| 294 | { | ||
| 295 | return buffer + sprintf(buffer, "voluntary_ctxt_switches:\t%lu\n" | ||
| 296 | "nonvoluntary_ctxt_switches:\t%lu\n", | ||
| 297 | p->nvcsw, | ||
| 298 | p->nivcsw); | ||
| 299 | } | ||
| 300 | |||
| 292 | int proc_pid_status(struct task_struct *task, char * buffer) | 301 | int proc_pid_status(struct task_struct *task, char * buffer) |
| 293 | { | 302 | { |
| 294 | char * orig = buffer; | 303 | char * orig = buffer; |
| @@ -307,6 +316,7 @@ int proc_pid_status(struct task_struct *task, char * buffer) | |||
| 307 | #if defined(CONFIG_S390) | 316 | #if defined(CONFIG_S390) |
| 308 | buffer = task_show_regs(task, buffer); | 317 | buffer = task_show_regs(task, buffer); |
| 309 | #endif | 318 | #endif |
| 319 | buffer = task_context_switch_counts(task, buffer); | ||
| 310 | return buffer - orig; | 320 | return buffer - orig; |
| 311 | } | 321 | } |
| 312 | 322 | ||
diff --git a/include/linux/taskstats.h b/include/linux/taskstats.h index a46104a28f66..dce1ed204972 100644 --- a/include/linux/taskstats.h +++ b/include/linux/taskstats.h | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | */ | 31 | */ |
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | #define TASKSTATS_VERSION 4 | 34 | #define TASKSTATS_VERSION 5 |
| 35 | #define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN | 35 | #define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN |
| 36 | * in linux/sched.h */ | 36 | * in linux/sched.h */ |
| 37 | 37 | ||
| @@ -149,6 +149,9 @@ struct taskstats { | |||
| 149 | __u64 read_bytes; /* bytes of read I/O */ | 149 | __u64 read_bytes; /* bytes of read I/O */ |
| 150 | __u64 write_bytes; /* bytes of write I/O */ | 150 | __u64 write_bytes; /* bytes of write I/O */ |
| 151 | __u64 cancelled_write_bytes; /* bytes of cancelled write I/O */ | 151 | __u64 cancelled_write_bytes; /* bytes of cancelled write I/O */ |
| 152 | |||
| 153 | __u64 nvcsw; /* voluntary_ctxt_switches */ | ||
| 154 | __u64 nivcsw; /* nonvoluntary_ctxt_switches */ | ||
| 152 | }; | 155 | }; |
| 153 | 156 | ||
| 154 | 157 | ||
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 906cae771585..059431ed67db 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c | |||
| @@ -196,6 +196,8 @@ static int fill_pid(pid_t pid, struct task_struct *tsk, | |||
| 196 | 196 | ||
| 197 | /* fill in basic acct fields */ | 197 | /* fill in basic acct fields */ |
| 198 | stats->version = TASKSTATS_VERSION; | 198 | stats->version = TASKSTATS_VERSION; |
| 199 | stats->nvcsw = tsk->nvcsw; | ||
| 200 | stats->nivcsw = tsk->nivcsw; | ||
| 199 | bacct_add_tsk(stats, tsk); | 201 | bacct_add_tsk(stats, tsk); |
| 200 | 202 | ||
| 201 | /* fill in extended acct fields */ | 203 | /* fill in extended acct fields */ |
| @@ -242,6 +244,8 @@ static int fill_tgid(pid_t tgid, struct task_struct *first, | |||
| 242 | */ | 244 | */ |
| 243 | delayacct_add_tsk(stats, tsk); | 245 | delayacct_add_tsk(stats, tsk); |
| 244 | 246 | ||
| 247 | stats->nvcsw += tsk->nvcsw; | ||
| 248 | stats->nivcsw += tsk->nivcsw; | ||
| 245 | } while_each_thread(first, tsk); | 249 | } while_each_thread(first, tsk); |
| 246 | 250 | ||
| 247 | unlock_task_sighand(first, &flags); | 251 | unlock_task_sighand(first, &flags); |
