]>
Commit | Line | Data |
---|---|---|
94e7e258 KZ |
1 | /* |
2 | * irq-common.c - functions to display kernel interrupt information. | |
3 | * | |
4 | * Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com> | |
5 | * Copyright (C) 2020 Karel Zak <kzak@redhat.com> | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
22 | #include <ctype.h> | |
23 | #include <errno.h> | |
24 | #include <limits.h> | |
25 | #include <locale.h> | |
4b2fadb1 | 26 | #include <stdbool.h> |
94e7e258 KZ |
27 | #include <stdio.h> |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <sys/types.h> | |
31 | #include <unistd.h> | |
32 | ||
33 | #include <libsmartcols.h> | |
34 | ||
35 | #include "c.h" | |
36 | #include "nls.h" | |
37 | #include "pathnames.h" | |
38 | #include "strutils.h" | |
39 | #include "xalloc.h" | |
40 | ||
41 | #include "irq-common.h" | |
42 | ||
43 | #define IRQ_INFO_LEN 64 | |
44 | ||
45 | struct colinfo { | |
46 | const char *name; | |
47 | double whint; | |
48 | int flags; | |
49 | const char *help; | |
50 | int json_type; | |
51 | }; | |
52 | ||
53 | static const struct colinfo infos[] = { | |
54 | [COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, | |
55 | [COL_TOTAL] = {"TOTAL", 0.10, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, | |
56 | [COL_DELTA] = {"DELTA", 0.10, SCOLS_FL_RIGHT, N_("delta count"), SCOLS_JSON_NUMBER}, | |
57 | [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, | |
58 | }; | |
59 | ||
b2a49bc2 | 60 | /* make softirq friendly to end-user */ |
61 | struct softirq_desc { | |
62 | char *irq; | |
63 | char *desc; | |
64 | } softirq_descs[] = { | |
65 | { .irq = "HI", .desc = "high priority tasklet softirq" }, | |
66 | { .irq = "TIMER", .desc = "timer softirq" }, | |
67 | { .irq = "NET_TX", .desc = "network transmit softirq", }, | |
68 | { .irq = "NET_RX", .desc = "network receive softirq" }, | |
69 | { .irq = "BLOCK", .desc = "block device softirq" }, | |
70 | { .irq = "IRQ_POLL", .desc = "IO poll softirq" }, | |
71 | { .irq = "TASKLET", .desc = "normal priority tasklet softirq" }, | |
72 | { .irq = "SCHED", .desc = "schedule softirq" }, | |
73 | { .irq = "HRTIMER", .desc = "high resolution timer softirq" }, | |
74 | { .irq = "RCU", .desc = "RCU softirq" }, | |
75 | }; | |
76 | ||
77 | static void get_softirq_desc(struct irq_info *curr) | |
78 | { | |
79 | int i, size = ARRAY_SIZE(softirq_descs); | |
80 | ||
81 | for (i = 0; i < size; i++) { | |
82 | if (!strcmp(curr->irq, softirq_descs[i].irq)) | |
83 | break; | |
84 | } | |
85 | ||
86 | if (i < size) | |
87 | curr->name = xstrdup(softirq_descs[i].desc); | |
88 | else | |
89 | curr->name = xstrdup(""); | |
90 | } | |
91 | ||
9c0740fc | 92 | int irq_column_name_to_id(const char *name, size_t namesz) |
94e7e258 KZ |
93 | { |
94 | size_t i; | |
95 | ||
96 | assert(name); | |
97 | for (i = 0; i < ARRAY_SIZE(infos); i++) { | |
98 | const char *cn = infos[i].name; | |
99 | ||
100 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
101 | return i; | |
102 | } | |
103 | warnx(_("unknown column: %s"), name); | |
104 | return -1; | |
105 | } | |
106 | ||
107 | static inline int get_column_id(struct irq_output *out, size_t const num) | |
108 | { | |
109 | assert(num < out->ncolumns); | |
110 | assert(out->columns[num] < (int)ARRAY_SIZE(infos)); | |
111 | ||
112 | return out->columns[num]; | |
113 | } | |
114 | ||
115 | static inline const struct colinfo *get_column_info( | |
116 | struct irq_output *out, unsigned num) | |
117 | { | |
118 | return &infos[get_column_id(out, num)]; | |
119 | } | |
120 | ||
a0f62b0b | 121 | void irq_print_columns(FILE *f, int nodelta) |
94e7e258 KZ |
122 | { |
123 | size_t i; | |
124 | ||
a0f62b0b KZ |
125 | for (i = 0; i < ARRAY_SIZE(infos); i++) { |
126 | if (nodelta && i == COL_DELTA) | |
127 | continue; | |
94e7e258 | 128 | fprintf(f, " %-5s %s\n", infos[i].name, _(infos[i].help)); |
a0f62b0b | 129 | } |
94e7e258 KZ |
130 | } |
131 | ||
132 | static struct libscols_table *new_scols_table(struct irq_output *out) | |
133 | { | |
134 | size_t i; | |
135 | struct libscols_table *table; | |
136 | ||
137 | table = scols_new_table(); | |
138 | if (!table) { | |
139 | warn(_("failed to initialize output table")); | |
140 | return NULL; | |
141 | } | |
142 | scols_table_enable_json(table, out->json); | |
143 | scols_table_enable_noheadings(table, out->no_headings); | |
5a20c0de | 144 | scols_table_enable_export(table, out->pairs); |
94e7e258 KZ |
145 | |
146 | if (out->json) | |
a23aecc1 | 147 | scols_table_set_name(table, "interrupts"); |
94e7e258 KZ |
148 | |
149 | for (i = 0; i < out->ncolumns; i++) { | |
150 | const struct colinfo *col = get_column_info(out, i); | |
151 | int flags = col->flags; | |
152 | struct libscols_column *cl; | |
153 | ||
154 | cl = scols_table_new_column(table, col->name, col->whint, flags); | |
155 | if (cl == NULL) { | |
156 | warnx(_("failed to initialize output column")); | |
157 | goto err; | |
158 | } | |
159 | if (out->json) | |
160 | scols_column_set_json_type(cl, col->json_type); | |
161 | } | |
162 | ||
163 | return table; | |
164 | err: | |
165 | scols_unref_table(table); | |
166 | return NULL; | |
167 | } | |
168 | ||
61074478 KZ |
169 | static struct libscols_line *new_scols_line(struct libscols_table *table) |
170 | { | |
171 | struct libscols_line *line = scols_table_new_line(table, NULL); | |
172 | if (!line) { | |
173 | warn(_("failed to add line to output")); | |
174 | return NULL; | |
175 | } | |
176 | return line; | |
177 | } | |
178 | ||
94e7e258 | 179 | static void add_scols_line(struct irq_output *out, |
77f57b90 | 180 | struct irq_info *info, |
94e7e258 KZ |
181 | struct libscols_table *table) |
182 | { | |
183 | size_t i; | |
61074478 | 184 | struct libscols_line *line = new_scols_line(table); |
94e7e258 KZ |
185 | |
186 | for (i = 0; i < out->ncolumns; i++) { | |
187 | char *str = NULL; | |
188 | ||
189 | switch (get_column_id(out, i)) { | |
190 | case COL_IRQ: | |
77f57b90 | 191 | xasprintf(&str, "%s", info->irq); |
94e7e258 KZ |
192 | break; |
193 | case COL_TOTAL: | |
77f57b90 | 194 | xasprintf(&str, "%ld", info->total); |
94e7e258 KZ |
195 | break; |
196 | case COL_DELTA: | |
77f57b90 | 197 | xasprintf(&str, "%ld", info->delta); |
94e7e258 KZ |
198 | break; |
199 | case COL_NAME: | |
77f57b90 | 200 | xasprintf(&str, "%s", info->name); |
94e7e258 KZ |
201 | break; |
202 | default: | |
203 | break; | |
204 | } | |
205 | ||
206 | if (str && scols_line_refer_data(line, i, str) != 0) | |
207 | err_oom(); | |
208 | } | |
94e7e258 KZ |
209 | } |
210 | ||
211 | static char *remove_repeated_spaces(char *str) | |
212 | { | |
213 | char *inp = str, *outp = str; | |
214 | uint8_t prev_space = 0; | |
215 | ||
216 | while (*inp) { | |
217 | if (isspace(*inp)) { | |
218 | if (!prev_space) { | |
219 | *outp++ = ' '; | |
220 | prev_space = 1; | |
221 | } | |
222 | } else { | |
223 | *outp++ = *inp; | |
224 | prev_space = 0; | |
225 | } | |
226 | ++inp; | |
227 | } | |
228 | *outp = '\0'; | |
229 | return str; | |
230 | } | |
231 | ||
4b2fadb1 | 232 | static bool cpu_in_list(int cpu, size_t setsize, cpu_set_t *cpuset) |
233 | { | |
234 | /* no -C/--cpu-list specified, use all the CPUs */ | |
235 | if (!cpuset) | |
236 | return true; | |
237 | ||
238 | return CPU_ISSET_S(cpu, setsize, cpuset); | |
239 | } | |
240 | ||
94e7e258 KZ |
241 | /* |
242 | * irqinfo - parse the system's interrupts | |
243 | */ | |
4b2fadb1 | 244 | static struct irq_stat *get_irqinfo(int softirq, size_t setsize, cpu_set_t *cpuset) |
94e7e258 KZ |
245 | { |
246 | FILE *irqfile; | |
247 | char *line = NULL, *tmp; | |
248 | size_t len = 0; | |
249 | struct irq_stat *stat; | |
250 | struct irq_info *curr; | |
251 | ||
252 | /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */ | |
253 | stat = xcalloc(1, sizeof(*stat)); | |
254 | ||
255 | stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); | |
256 | stat->nr_irq_info = IRQ_INFO_LEN; | |
257 | ||
b6ce063b | 258 | if (softirq) |
259 | irqfile = fopen(_PATH_PROC_SOFTIRQS, "r"); | |
260 | else | |
261 | irqfile = fopen(_PATH_PROC_INTERRUPTS, "r"); | |
94e7e258 KZ |
262 | if (!irqfile) { |
263 | warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS); | |
264 | goto free_stat; | |
265 | } | |
266 | ||
267 | /* read header firstly */ | |
268 | if (getline(&line, &len, irqfile) < 0) { | |
269 | warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS); | |
270 | goto close_file; | |
271 | } | |
272 | ||
273 | tmp = line; | |
274 | while ((tmp = strstr(tmp, "CPU")) != NULL) { | |
275 | tmp += 3; /* skip this "CPU", find next */ | |
276 | stat->nr_active_cpu++; | |
277 | } | |
278 | ||
a23aecc1 KZ |
279 | stat->cpus = xcalloc(stat->nr_active_cpu, sizeof(struct irq_cpu)); |
280 | ||
94e7e258 KZ |
281 | /* parse each line of _PATH_PROC_INTERRUPTS */ |
282 | while (getline(&line, &len, irqfile) >= 0) { | |
283 | unsigned long count; | |
a23aecc1 KZ |
284 | size_t index; |
285 | int length; | |
94e7e258 KZ |
286 | |
287 | tmp = strchr(line, ':'); | |
288 | if (!tmp) | |
289 | continue; | |
290 | ||
291 | length = strlen(line); | |
292 | ||
293 | curr = stat->irq_info + stat->nr_irq++; | |
294 | memset(curr, 0, sizeof(*curr)); | |
295 | *tmp = '\0'; | |
296 | curr->irq = xstrdup(line); | |
297 | ltrim_whitespace((unsigned char *)curr->irq); | |
298 | ||
299 | tmp += 1; | |
300 | for (index = 0; (index < stat->nr_active_cpu) && (tmp - line < length); index++) { | |
a23aecc1 KZ |
301 | struct irq_cpu *cpu = &stat->cpus[index]; |
302 | ||
b86ed624 KZ |
303 | if (sscanf(tmp, " %10lu", &count) != 1) |
304 | continue; | |
4b2fadb1 | 305 | if (cpu_in_list(index, setsize, cpuset)) { |
306 | curr->total += count; | |
307 | cpu->total += count; | |
308 | stat->total_irq += count; | |
309 | } | |
a23aecc1 | 310 | |
94e7e258 KZ |
311 | tmp += 11; |
312 | } | |
313 | ||
b2a49bc2 | 314 | /* softirq always has no desc, add additional desc for softirq */ |
315 | if (softirq) | |
316 | get_softirq_desc(curr); | |
317 | else { | |
318 | if (tmp - line < length) { | |
319 | /* strip all space before desc */ | |
320 | while (isspace(*tmp)) | |
321 | tmp++; | |
322 | tmp = remove_repeated_spaces(tmp); | |
323 | rtrim_whitespace((unsigned char *)tmp); | |
324 | curr->name = xstrdup(tmp); | |
325 | } else /* no irq name string, we have to set '\0' here */ | |
326 | curr->name = xstrdup(""); | |
327 | } | |
94e7e258 KZ |
328 | |
329 | if (stat->nr_irq == stat->nr_irq_info) { | |
330 | stat->nr_irq_info *= 2; | |
64d6d400 TW |
331 | stat->irq_info = xreallocarray(stat->irq_info, stat->nr_irq_info, |
332 | sizeof(*stat->irq_info)); | |
94e7e258 KZ |
333 | } |
334 | } | |
335 | fclose(irqfile); | |
336 | free(line); | |
337 | return stat; | |
338 | ||
339 | close_file: | |
340 | fclose(irqfile); | |
341 | free_stat: | |
342 | free(stat->irq_info); | |
a23aecc1 | 343 | free(stat->cpus); |
94e7e258 KZ |
344 | free(stat); |
345 | free(line); | |
346 | return NULL; | |
347 | } | |
348 | ||
77f57b90 | 349 | void free_irqstat(struct irq_stat *stat) |
94e7e258 | 350 | { |
77f57b90 KZ |
351 | size_t i; |
352 | ||
353 | if (!stat) | |
354 | return; | |
355 | ||
356 | for (i = 0; i < stat->nr_irq; i++) { | |
357 | free(stat->irq_info[i].name); | |
358 | free(stat->irq_info[i].irq); | |
359 | } | |
360 | ||
361 | free(stat->irq_info); | |
a23aecc1 | 362 | free(stat->cpus); |
94e7e258 KZ |
363 | free(stat); |
364 | } | |
365 | ||
44e39c99 KZ |
366 | static inline int cmp_name(const struct irq_info *a, |
367 | const struct irq_info *b) | |
368 | { | |
278e00b4 | 369 | return strcoll(a->name, b->name); |
44e39c99 KZ |
370 | } |
371 | ||
65ca5080 VU |
372 | static inline int cmp_ulong_descending(unsigned long a, |
373 | unsigned long b) | |
374 | { | |
375 | if (a == b) | |
376 | return 0; | |
377 | if (a < b) | |
378 | return 1; | |
379 | else | |
380 | return -1; | |
381 | } | |
382 | ||
44e39c99 KZ |
383 | static inline int cmp_total(const struct irq_info *a, |
384 | const struct irq_info *b) | |
385 | { | |
65ca5080 VU |
386 | int cmp = cmp_ulong_descending(a->total, b->total); |
387 | return cmp ? cmp : cmp_name(a, b); | |
44e39c99 KZ |
388 | } |
389 | ||
390 | static inline int cmp_delta(const struct irq_info *a, | |
391 | const struct irq_info *b) | |
392 | { | |
65ca5080 VU |
393 | int cmp = cmp_ulong_descending(a->delta, b->delta); |
394 | return cmp ? cmp : cmp_name(a, b); | |
44e39c99 KZ |
395 | } |
396 | ||
397 | static inline int cmp_interrupts(const struct irq_info *a, | |
398 | const struct irq_info *b) | |
399 | { | |
278e00b4 | 400 | return strverscmp(a->irq, b->irq); |
44e39c99 KZ |
401 | } |
402 | ||
94e7e258 KZ |
403 | static void sort_result(struct irq_output *out, |
404 | struct irq_info *result, | |
405 | size_t nmemb) | |
406 | { | |
44e39c99 KZ |
407 | irq_cmp_t *func = cmp_total; /* default */ |
408 | ||
409 | if (out->sort_cmp_func) | |
410 | func = out->sort_cmp_func; | |
411 | ||
94e7e258 | 412 | qsort(result, nmemb, sizeof(*result), |
44e39c99 KZ |
413 | (int (*)(const void *, const void *)) func); |
414 | } | |
415 | ||
416 | void set_sort_func_by_name(struct irq_output *out, const char *name) | |
417 | { | |
418 | if (strcasecmp(name, "IRQ") == 0) | |
419 | out->sort_cmp_func = cmp_interrupts; | |
420 | else if (strcasecmp(name, "TOTAL") == 0) | |
421 | out->sort_cmp_func = cmp_total; | |
422 | else if (strcasecmp(name, "DELTA") == 0) | |
423 | out->sort_cmp_func = cmp_delta; | |
424 | else if (strcasecmp(name, "NAME") == 0) | |
425 | out->sort_cmp_func = cmp_name; | |
426 | else | |
311e33af | 427 | errx(EXIT_FAILURE, _("unsupported column name to sort output")); |
94e7e258 KZ |
428 | } |
429 | ||
44e39c99 | 430 | void set_sort_func_by_key(struct irq_output *out, char c) |
94e7e258 | 431 | { |
44e39c99 | 432 | switch (c) { |
94e7e258 | 433 | case 'i': |
44e39c99 KZ |
434 | out->sort_cmp_func = cmp_interrupts; |
435 | break; | |
94e7e258 | 436 | case 't': |
44e39c99 KZ |
437 | out->sort_cmp_func = cmp_total; |
438 | break; | |
94e7e258 | 439 | case 'd': |
44e39c99 KZ |
440 | out->sort_cmp_func = cmp_delta; |
441 | break; | |
94e7e258 | 442 | case 'n': |
44e39c99 KZ |
443 | out->sort_cmp_func = cmp_name; |
444 | break; | |
94e7e258 KZ |
445 | } |
446 | } | |
447 | ||
a23aecc1 KZ |
448 | struct libscols_table *get_scols_cpus_table(struct irq_output *out, |
449 | struct irq_stat *prev, | |
4b2fadb1 | 450 | struct irq_stat *curr, |
451 | size_t setsize, | |
452 | cpu_set_t *cpuset) | |
a23aecc1 KZ |
453 | { |
454 | struct libscols_table *table; | |
455 | struct libscols_column *cl; | |
456 | struct libscols_line *ln; | |
b7865ae1 | 457 | char colname[sizeof("cpu") + sizeof(stringify_value(LONG_MAX))]; |
4b2fadb1 | 458 | size_t i, j; |
a23aecc1 KZ |
459 | |
460 | if (prev) { | |
461 | for (i = 0; i < curr->nr_active_cpu; i++) { | |
462 | struct irq_cpu *pre = &prev->cpus[i]; | |
463 | struct irq_cpu *cur = &curr->cpus[i]; | |
464 | ||
465 | cur->delta = cur->total - pre->total; | |
466 | } | |
467 | } | |
468 | ||
469 | table = scols_new_table(); | |
470 | if (!table) { | |
471 | warn(_("failed to initialize output table")); | |
472 | return NULL; | |
473 | } | |
474 | scols_table_enable_json(table, out->json); | |
475 | scols_table_enable_noheadings(table, out->no_headings); | |
476 | scols_table_enable_export(table, out->pairs); | |
477 | ||
478 | if (out->json) | |
479 | scols_table_set_name(table, _("cpu-interrupts")); | |
480 | else | |
481 | scols_table_new_column(table, "", 0, SCOLS_FL_RIGHT); | |
482 | ||
483 | for (i = 0; i < curr->nr_active_cpu; i++) { | |
4b2fadb1 | 484 | if (!cpu_in_list(i, setsize, cpuset)) |
485 | continue; | |
a23aecc1 KZ |
486 | snprintf(colname, sizeof(colname), "cpu%zu", i); |
487 | cl = scols_table_new_column(table, colname, 0, SCOLS_FL_RIGHT); | |
488 | if (cl == NULL) { | |
489 | warnx(_("failed to initialize output column")); | |
490 | goto err; | |
491 | } | |
492 | if (out->json) | |
493 | scols_column_set_json_type(cl, SCOLS_JSON_STRING); | |
494 | } | |
495 | ||
496 | /* per cpu % of total */ | |
61074478 | 497 | ln = new_scols_line(table); |
55d2dfa1 | 498 | if (!ln || (!out->json && scols_line_set_data(ln, 0, "%irq:") != 0)) |
a23aecc1 | 499 | goto err; |
a23aecc1 | 500 | |
4b2fadb1 | 501 | for (i = 0, j = 0; i < curr->nr_active_cpu; i++) { |
a23aecc1 KZ |
502 | struct irq_cpu *cpu = &curr->cpus[i]; |
503 | char *str; | |
504 | ||
4b2fadb1 | 505 | if (!cpu_in_list(i, setsize, cpuset)) |
506 | continue; | |
a23aecc1 | 507 | xasprintf(&str, "%0.1f", (double)((long double) cpu->total / (long double) curr->total_irq * 100.0)); |
4b2fadb1 | 508 | if (str && scols_line_refer_data(ln, ++j, str) != 0) |
a23aecc1 KZ |
509 | goto err; |
510 | } | |
511 | ||
512 | /* per cpu % of delta */ | |
61074478 | 513 | ln = new_scols_line(table); |
b97e2145 | 514 | /* xgettext:no-c-format */ |
55d2dfa1 | 515 | if (!ln || (!out->json && scols_line_set_data(ln, 0, _("%delta:")) != 0)) |
a23aecc1 | 516 | goto err; |
a23aecc1 | 517 | |
4b2fadb1 | 518 | for (i = 0, j = 0; i < curr->nr_active_cpu; i++) { |
a23aecc1 KZ |
519 | struct irq_cpu *cpu = &curr->cpus[i]; |
520 | char *str; | |
521 | ||
4b2fadb1 | 522 | if (!cpu_in_list(i, setsize, cpuset)) |
523 | continue; | |
a23aecc1 KZ |
524 | if (!curr->delta_irq) |
525 | continue; | |
526 | xasprintf(&str, "%0.1f", (double)((long double) cpu->delta / (long double) curr->delta_irq * 100.0)); | |
4b2fadb1 | 527 | if (str && scols_line_refer_data(ln, ++j, str) != 0) |
a23aecc1 KZ |
528 | goto err; |
529 | } | |
530 | ||
531 | return table; | |
532 | err: | |
533 | scols_unref_table(table); | |
534 | return NULL; | |
535 | } | |
536 | ||
94e7e258 KZ |
537 | struct libscols_table *get_scols_table(struct irq_output *out, |
538 | struct irq_stat *prev, | |
b6ce063b | 539 | struct irq_stat **xstat, |
4b2fadb1 | 540 | int softirq, |
541 | size_t setsize, | |
542 | cpu_set_t *cpuset) | |
94e7e258 KZ |
543 | { |
544 | struct libscols_table *table; | |
77f57b90 | 545 | struct irq_info *result; |
94e7e258 KZ |
546 | struct irq_stat *stat; |
547 | size_t size; | |
77f57b90 | 548 | size_t i; |
94e7e258 KZ |
549 | |
550 | /* the stats */ | |
4b2fadb1 | 551 | stat = get_irqinfo(softirq, setsize, cpuset); |
94e7e258 KZ |
552 | if (!stat) |
553 | return NULL; | |
554 | ||
555 | size = sizeof(*stat->irq_info) * stat->nr_irq; | |
556 | result = xmalloc(size); | |
557 | memcpy(result, stat->irq_info, size); | |
558 | ||
559 | if (prev) { | |
560 | stat->delta_irq = 0; | |
77f57b90 KZ |
561 | for (i = 0; i < stat->nr_irq; i++) { |
562 | struct irq_info *cur = &result[i]; | |
563 | struct irq_info *pre = &prev->irq_info[i]; | |
564 | ||
565 | cur->delta = cur->total - pre->total; | |
566 | stat->delta_irq += cur->delta; | |
94e7e258 KZ |
567 | } |
568 | } | |
569 | sort_result(out, result, stat->nr_irq); | |
570 | ||
571 | table = new_scols_table(out); | |
e37babc0 KZ |
572 | if (!table) { |
573 | free(result); | |
01a895d8 | 574 | free_irqstat(stat); |
94e7e258 | 575 | return NULL; |
e37babc0 | 576 | } |
94e7e258 | 577 | |
77f57b90 KZ |
578 | for (i = 0; i < stat->nr_irq; i++) |
579 | add_scols_line(out, &result[i], table); | |
94e7e258 KZ |
580 | |
581 | free(result); | |
582 | ||
583 | if (xstat) | |
584 | *xstat = stat; | |
585 | else | |
77f57b90 | 586 | free_irqstat(stat); |
94e7e258 KZ |
587 | |
588 | return table; | |
589 | } |