commit f852f5ce4d28f88308f0e555c067e63e3edd7f37
Author: Dave Anderson <anderson@redhat.com>
Date: Fri Nov 3 09:21:22 2017 -0400
Fix for a "ps -l" regression introduced by the new "ps -y" option
introduced above. Without the patch, the -l option generates a
segmentation violation if not accompanied by a -C cpu specifier
option.
(vinayakm.list@gmail.com)
diff --git a/task.c b/task.c
index 5754159..f2628b7 100644
--- a/task.c
+++ b/task.c
@@ -3485,7 +3485,7 @@ show_last_run(struct task_context *tc, struct psinfo *psi)
sprintf(format, "[%c%dll%c] ", '%', c,
pc->output_radix == 10 ? 'u' : 'x');
- if (psi) {
+ if (psi && psi->cpus) {
for (c = others = 0; c < kt->cpus; c++) {
if (!NUM_IN_BITMAP(psi->cpus, c))
continue;
commit 7ac1368cdca0fc2013bb3963456fcd2574c7cdd7
Author: Dave Anderson <anderson@redhat.com>
Date: Mon Nov 6 10:48:40 2017 -0500
Fix for the "kmem -i" and "kmem -V" options in Linux 4.8 and later
kernels containing commit 75ef7184053989118d3814c558a9af62e7376a58,
titled "mm, vmstat: add infrastructure for per-node vmstats".
Without the patch, the CACHED line of "kmem -i" shows 0, and the
VM_STAT section of "kmem -V" is missing entirely.
(vinayakm.list@gmail.com)
diff --git a/memory.c b/memory.c
index ebd671a..3097558 100644
--- a/memory.c
+++ b/memory.c
@@ -17340,30 +17340,43 @@ vm_stat_init(void)
int c ATTRIBUTE_UNUSED;
struct gnu_request *req;
char *start;
- long enum_value;
+ long enum_value, zc = -1;
+ int split_vmstat = 0, ni = 0;
if (vt->flags & VM_STAT)
return TRUE;
- if ((vt->nr_vm_stat_items == -1) || !symbol_exists("vm_stat"))
+ if ((vt->nr_vm_stat_items == -1) ||
+ (!symbol_exists("vm_stat") && !symbol_exists("vm_zone_stat")))
goto bailout;
/*
* look for type: type = atomic_long_t []
*/
if (LKCD_KERNTYPES()) {
- if (!symbol_exists("vm_stat"))
+ if ((!symbol_exists("vm_stat") &&
+ !symbol_exists("vm_zone_stat")))
goto bailout;
/*
* Just assume that vm_stat is an array; there is
* no symbol info in a kerntypes file.
*/
} else {
- if (!symbol_exists("vm_stat") ||
- get_symbol_type("vm_stat", NULL, NULL) != TYPE_CODE_ARRAY)
+ if (symbol_exists("vm_stat") &&
+ get_symbol_type("vm_stat", NULL, NULL) == TYPE_CODE_ARRAY) {
+ vt->nr_vm_stat_items =
+ get_array_length("vm_stat", NULL, 0);
+ } else if (symbol_exists("vm_zone_stat") &&
+ get_symbol_type("vm_zone_stat",
+ NULL, NULL) == TYPE_CODE_ARRAY) {
+ vt->nr_vm_stat_items =
+ get_array_length("vm_zone_stat", NULL, 0)
+ + get_array_length("vm_node_stat", NULL, 0);
+ split_vmstat = 1;
+ enumerator_value("NR_VM_ZONE_STAT_ITEMS", &zc);
+ } else {
goto bailout;
-
- vt->nr_vm_stat_items = get_array_length("vm_stat", NULL, 0);
+ }
}
open_tmpfile();
@@ -17372,6 +17385,14 @@ vm_stat_init(void)
req->name = "zone_stat_item";
req->flags = GNU_PRINT_ENUMERATORS;
gdb_interface(req);
+
+ if (split_vmstat) {
+ req->command = GNU_GET_DATATYPE;
+ req->name = "node_stat_item";
+ req->flags = GNU_PRINT_ENUMERATORS;
+ gdb_interface(req);
+ }
+
FREEBUF(req);
stringlen = 1;
@@ -17383,11 +17404,17 @@ vm_stat_init(void)
continue;
clean_line(buf);
c = parse_line(buf, arglist);
- if (STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) {
+ if ((!split_vmstat &&
+ STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) ||
+ (split_vmstat &&
+ STREQ(arglist[0], "NR_VM_NODE_STAT_ITEMS"))) {
if (LKCD_KERNTYPES())
vt->nr_vm_stat_items =
MAX(atoi(arglist[2]), count);
break;
+ } else if (split_vmstat &&
+ STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) {
+ continue;
} else {
stringlen += strlen(arglist[0]);
count++;
@@ -17409,18 +17436,24 @@ vm_stat_init(void)
if (strstr(buf, "{") || strstr(buf, "}"))
continue;
c = parse_line(buf, arglist);
- if (enumerator_value(arglist[0], &enum_value))
- i = enum_value;
- else {
+ if (!enumerator_value(arglist[0], &enum_value)) {
close_tmpfile();
goto bailout;
}
+
+ i = ni + enum_value;
+ if (!ni && (enum_value == zc)) {
+ ni = zc;
+ continue;
+ }
+
if (i < vt->nr_vm_stat_items) {
vt->vm_stat_items[i] = start;
strcpy(start, arglist[0]);
start += strlen(arglist[0]) + 1;
}
}
+
close_tmpfile();
vt->flags |= VM_STAT;
@@ -17443,39 +17476,61 @@ dump_vm_stat(char *item, long *retval, ulong zone)
ulong *vp;
ulong location;
int i, maxlen, len;
+ long tc, zc = 0, nc = 0;
+ int split_vmstat = 0;
if (!vm_stat_init()) {
if (!item)
if (CRASHDEBUG(1))
- error(INFO,
+ error(INFO,
"vm_stat not available in this kernel\n");
return FALSE;
}
buf = GETBUF(sizeof(ulong) * vt->nr_vm_stat_items);
- location = zone ? zone : symbol_value("vm_stat");
-
- readmem(location, KVADDR, buf,
- sizeof(ulong) * vt->nr_vm_stat_items,
- "vm_stat", FAULT_ON_ERROR);
+ if (symbol_exists("vm_node_stat") && symbol_exists("vm_zone_stat"))
+ split_vmstat = 1;
+ else
+ location = zone ? zone : symbol_value("vm_stat");
+
+ if (split_vmstat) {
+ enumerator_value("NR_VM_ZONE_STAT_ITEMS", &zc);
+ location = zone ? zone : symbol_value("vm_zone_stat");
+ readmem(location, KVADDR, buf,
+ sizeof(ulong) * zc,
+ "vm_zone_stat", FAULT_ON_ERROR);
+ if (!zone) {
+ location = symbol_value("vm_node_stat");
+ enumerator_value("NR_VM_NODE_STAT_ITEMS", &nc);
+ readmem(location, KVADDR, buf + (sizeof(ulong) * zc),
+ sizeof(ulong) * nc,
+ "vm_node_stat", FAULT_ON_ERROR);
+ }
+ tc = zc + nc;
+ } else {
+ readmem(location, KVADDR, buf,
+ sizeof(ulong) * vt->nr_vm_stat_items,
+ "vm_stat", FAULT_ON_ERROR);
+ tc = vt->nr_vm_stat_items;
+ }
if (!item) {
if (!zone)
fprintf(fp, " VM_STAT:\n");
- for (i = maxlen = 0; i < vt->nr_vm_stat_items; i++)
+ for (i = maxlen = 0; i < tc; i++)
if ((len = strlen(vt->vm_stat_items[i])) > maxlen)
maxlen = len;
vp = (ulong *)buf;
- for (i = 0; i < vt->nr_vm_stat_items; i++)
- fprintf(fp, "%s%s: %ld\n",
+ for (i = 0; i < tc; i++)
+ fprintf(fp, "%s%s: %ld\n",
space(maxlen - strlen(vt->vm_stat_items[i])),
vt->vm_stat_items[i], vp[i]);
return TRUE;
}
vp = (ulong *)buf;
- for (i = 0; i < vt->nr_vm_stat_items; i++) {
+ for (i = 0; i < tc; i++) {
if (STREQ(vt->vm_stat_items[i], item)) {
*retval = vp[i];
return TRUE;
commit 333df037bc72aa81faf0904aaea29d43be2c724d
Author: Dave Anderson <anderson@redhat.com>
Date: Mon Nov 6 11:01:45 2017 -0500
Fix for Linux 4.11 and later kernels that contain kernel commit
4b3ef9daa4fc0bba742a79faecb17fdaaead083b, titled "mm/swap: split
swap cache into 64MB trunks". Without the patch, the CACHED line
of "kmem -i" may show nonsensical data.
(vinayakm.list@gmail.com)
diff --git a/memory.c b/memory.c
index 3097558..7537c43 100644
--- a/memory.c
+++ b/memory.c
@@ -8236,7 +8236,44 @@ dump_kmeminfo(void)
char *swapper_space = GETBUF(SIZE(address_space));
swapper_space_nrpages = 0;
- if (symbol_exists("swapper_spaces") &&
+ if (symbol_exists("nr_swapper_spaces") &&
+ (len = get_array_length("nr_swapper_spaces",
+ NULL, 0))) {
+ char *nr_swapper_space =
+ GETBUF(len * sizeof(unsigned int));
+ readmem(symbol_value("nr_swapper_spaces"), KVADDR,
+ nr_swapper_space, len * sizeof(unsigned int),
+ "nr_swapper_space", RETURN_ON_ERROR);
+ for (i = 0; i < len; i++) {
+ int j;
+ unsigned long sa;
+ unsigned int banks = UINT(nr_swapper_space +
+ (i * sizeof(unsigned int)));
+
+ if (!banks)
+ continue;
+
+ readmem(symbol_value("swapper_spaces") +
+ (i * sizeof(void *)),KVADDR,
+ &sa, sizeof(void *),
+ "swapper_space", RETURN_ON_ERROR);
+
+ if (!sa)
+ continue;
+
+ for (j = 0; j < banks; j++) {
+ readmem(sa + j * SIZE(address_space),
+ KVADDR, swapper_space,
+ SIZE(address_space),
+ "swapper_space",
+ RETURN_ON_ERROR);
+ swapper_space_nrpages +=
+ ULONG(swapper_space +
+ OFFSET(address_space_nrpages));
+ }
+ }
+ FREEBUF(nr_swapper_space);
+ } else if (symbol_exists("swapper_spaces") &&
(len = get_array_length("swapper_spaces", NULL, 0))) {
for (i = 0; i < len; i++) {
if (!readmem(symbol_value("swapper_spaces") +
@@ -8253,7 +8290,7 @@ dump_kmeminfo(void)
RETURN_ON_ERROR))
swapper_space_nrpages = ULONG(swapper_space +
OFFSET(address_space_nrpages));
-
+
page_cache_size = nr_file_pages - swapper_space_nrpages -
buffer_pages;
FREEBUF(swapper_space);
commit 613e5c7d6998c61880498537b4f288ef095cbe14
Author: Dave Anderson <anderson@redhat.com>
Date: Mon Nov 6 15:12:59 2017 -0500
Implemented a new "dev -D" option that is the same as "dev -d", but
filters out the display of disks that have no I/O in progress.
(oleksandr@redhat.com)
diff --git a/dev.c b/dev.c
index e46081e..3db898a 100644
--- a/dev.c
+++ b/dev.c
@@ -31,7 +31,7 @@ static const char *pci_strclass (uint, char *);
static const char *pci_strvendor(uint, char *);
static const char *pci_strdev(uint, uint, char *);
-static void diskio_option(void);
+static void diskio_option(ulong flags);
static struct dev_table {
ulong flags;
@@ -42,6 +42,9 @@ struct dev_table *dt = &dev_table;
#define DEV_INIT 0x1
#define DISKIO_INIT 0x2
+#define DIOF_ALL 1 << 0
+#define DIOF_NONZERO 1 << 1
+
void
dev_init(void)
{
@@ -93,11 +96,15 @@ cmd_dev(void)
flags = 0;
- while ((c = getopt(argcnt, args, "dpi")) != EOF) {
+ while ((c = getopt(argcnt, args, "dDpi")) != EOF) {
switch(c)
{
case 'd':
- diskio_option();
+ diskio_option(DIOF_ALL);
+ return;
+
+ case 'D':
+ diskio_option(DIOF_NONZERO);
return;
case 'i':
@@ -4002,7 +4009,7 @@ init_iter(struct iter *i)
}
static void
-display_one_diskio(struct iter *i, unsigned long gendisk)
+display_one_diskio(struct iter *i, unsigned long gendisk, ulong flags)
{
char disk_name[BUFSIZE + 1];
char buf0[BUFSIZE];
@@ -4028,6 +4035,10 @@ display_one_diskio(struct iter *i, unsigned long gendisk)
"gen_disk.major", FAULT_ON_ERROR);
i->get_diskio(queue_addr, &io);
+ if ((flags & DIOF_NONZERO)
+ && (io.read + io.write == 0))
+ return;
+
fprintf(fp, "%s%s%s %s%s%s%s %s%5d%s%s%s%s%s",
mkstring(buf0, 5, RJUST|INT_DEC, (char *)(unsigned long)major),
space(MINSPACE),
@@ -4055,7 +4066,7 @@ display_one_diskio(struct iter *i, unsigned long gendisk)
}
static void
-display_all_diskio(void)
+display_all_diskio(ulong flags)
{
struct iter i;
unsigned long gendisk;
@@ -4089,7 +4100,7 @@ display_all_diskio(void)
mkstring(buf5, 5, RJUST, "DRV"));
while ((gendisk = i.next_disk(&i)) != 0)
- display_one_diskio(&i, gendisk);
+ display_one_diskio(&i, gendisk, flags);
}
static
@@ -4149,8 +4160,8 @@ void diskio_init(void)
}
static void
-diskio_option(void)
+diskio_option(ulong flags)
{
diskio_init();
- display_all_diskio();
+ display_all_diskio(flags);
}
diff --git a/help.c b/help.c
index f7f61a1..fa01bfb 100644
--- a/help.c
+++ b/help.c
@@ -2722,7 +2722,7 @@ NULL
char *help_dev[] = {
"dev",
"device data",
-"[-i | -p | -d]",
+"[-i | -p | -d | -D]",
" If no argument is entered, this command dumps character and block",
" device data.\n",
" -i display I/O port usage; on 2.4 kernels, also display I/O memory usage.",
@@ -2736,6 +2736,7 @@ char *help_dev[] = {
" DRV: I/O requests that are in-flight in the device driver.",
" If the device driver uses blk-mq interface, this field",
" shows N/A(MQ).",
+" -D same as -d, but filter out disks with no in-progress I/O requests.",
"\nEXAMPLES",
" Display character and block device data:\n",
" %s> dev",
commit 57eaba59bff54ab3158d3a909e9f64551e27accf
Author: Dave Anderson <anderson@redhat.com>
Date: Wed Nov 8 14:22:16 2017 -0500
If a line number request for a module text address initially fails,
force the embedded gdb module to complete its two-stage strategy
used for reading debuginfo symbol tables from module object files,
and then retry the line number extraction. This automatically does
what the "mod -r" or "crash --readnow" options accomplish.
(anderson@redhat.com)
diff --git a/defs.h b/defs.h
index 967fce0..18f36b3 100644
--- a/defs.h
+++ b/defs.h
@@ -2688,6 +2688,7 @@ struct load_module {
struct syment *mod_init_symend;
ulong mod_percpu;
ulong mod_percpu_size;
+ struct objfile *loaded_objfile;
};
#define IN_MODULE(A,L) \
@@ -4479,6 +4480,7 @@ struct gnu_request {
struct symbol *sym;
struct objfile *obj;
} global_iterator;
+ struct load_module *lm;
};
/*
diff --git a/gdb-7.6.patch b/gdb-7.6.patch
index 094f01a..6aeffda 100644
--- a/gdb-7.6.patch
+++ b/gdb-7.6.patch
@@ -2323,3 +2323,72 @@ diff -up gdb-7.6/opcodes/configure.orig gdb-7.6/opcodes/configure
NO_WERROR="-Wno-error"
fi
+--- gdb-7.6/gdb/symtab.c.orig
++++ gdb-7.6/gdb/symtab.c
+@@ -5266,6 +5266,7 @@ gdb_get_line_number(struct gnu_request *
+ {
+ struct symtab_and_line sal;
+ struct symbol *sym;
++ struct objfile *objfile;
+ CORE_ADDR pc;
+
+ #define LASTCHAR(s) (s[strlen(s)-1])
+@@ -5281,8 +5282,22 @@ gdb_get_line_number(struct gnu_request *
+ sal = find_pc_line(pc, 0);
+
+ if (!sal.symtab) {
+- req->buf[0] = '\0';
+- return;
++ /*
++ * If a module address line number can't be found, it's typically
++ * due to its addrmap still containing offset values because its
++ * objfile doesn't have full symbols loaded.
++ */
++ if (req->lm) {
++ objfile = req->lm->loaded_objfile;
++ if (!objfile_has_full_symbols(objfile) && objfile->sf) {
++ objfile->sf->qf->expand_all_symtabs(objfile);
++ sal = find_pc_line(pc, 0);
++ }
++ }
++ if (!sal.symtab) {
++ req->buf[0] = '\0';
++ return;
++ }
+ }
+
+ if (sal.symtab->filename && sal.symtab->dirname) {
+@@ -5557,7 +5572,6 @@ struct load_module *gdb_current_load_mod
+ static void
+ gdb_add_symbol_file(struct gnu_request *req)
+ {
+- register struct objfile *loaded_objfile = NULL;
+ register struct objfile *objfile;
+ register struct minimal_symbol *m;
+ struct load_module *lm;
+@@ -5576,6 +5590,7 @@ gdb_add_symbol_file(struct gnu_request *
+
+ req->name = lm->mod_namelist;
+ gdb_delete_symbol_file(req);
++ lm->loaded_objfile = NULL;
+
+ if ((lm->mod_flags & MOD_NOPATCH) == 0) {
+ for (i = 0 ; i < lm->mod_sections; i++) {
+@@ -5623,12 +5638,15 @@ gdb_add_symbol_file(struct gnu_request *
+
+ ALL_OBJFILES(objfile) {
+ if (same_file(objfile->name, lm->mod_namelist)) {
+- loaded_objfile = objfile;
++ if (objfile->separate_debug_objfile)
++ lm->loaded_objfile = objfile->separate_debug_objfile;
++ else
++ lm->loaded_objfile = objfile;
+ break;
+ }
+ }
+
+- if (!loaded_objfile)
++ if (!lm->loaded_objfile)
+ req->flags |= GNU_COMMAND_FAILED;
+ }
+
diff --git a/symbols.c b/symbols.c
index 8a4c878..0d85ff7 100644
--- a/symbols.c
+++ b/symbols.c
@@ -3284,6 +3284,8 @@ dump_symbol_table(void)
lm->mod_section_data[s].size);
}
+ fprintf(fp, " loaded_objfile: %lx\n", (ulong)lm->loaded_objfile);
+
if (CRASHDEBUG(1)) {
for (sp = lm->mod_load_symtable;
sp < lm->mod_load_symend; sp++) {
@@ -4100,6 +4102,7 @@ get_line_number(ulong addr, char *buf, int reserved)
struct load_module *lm;
buf[0] = NULLCHAR;
+ lm = NULL;
if (NO_LINE_NUMBERS() || !is_kernel_text(addr))
return(buf);
@@ -4129,6 +4132,8 @@ get_line_number(ulong addr, char *buf, int reserved)
req->command = GNU_GET_LINE_NUMBER;
req->addr = addr;
req->buf = buf;
+ if (lm && lm->loaded_objfile)
+ req->lm = lm;
if ((sp = value_search(addr, NULL)))
req->name = sp->name;
gdb_interface(req);
@@ -12025,6 +12030,7 @@ delete_load_module(ulong base_addr)
if (lm->mod_section_data)
free(lm->mod_section_data);
lm->mod_section_data = (struct mod_section_data *)0;
+ lm->loaded_objfile = NULL;
}
st->flags &= ~LOAD_MODULE_SYMS;
return;
@@ -12061,6 +12067,7 @@ delete_load_module(ulong base_addr)
if (lm->mod_section_data)
free(lm->mod_section_data);
lm->mod_section_data = (struct mod_section_data *)0;
+ lm->loaded_objfile = NULL;
} else if (lm->mod_flags & MOD_LOAD_SYMS)
st->flags |= LOAD_MODULE_SYMS;
}
commit c8178eca9c74f81a7f803a58d339635cc152e8d9
Author: Dave Anderson <anderson@redhat.com>
Date: Thu Nov 9 11:39:05 2017 -0500
Update for support of Linux 4.14 and later PPC64 kernels where the
hash page table geometry accomodates a larger virtual address range.
Without the patch, the virtual-to-physical translation of user space
virtual addresses by "vm -p", "vtop", and "rd -u" may generate an
invalid translation or otherwise fail.
(hbathini@linux.vnet.ibm.com)
diff --git a/defs.h b/defs.h
index 18f36b3..9132075 100644
--- a/defs.h
+++ b/defs.h
@@ -3915,6 +3915,9 @@ struct efi_memory_desc_t {
#define PGD_INDEX_SIZE_L4_64K_3_10 12
#define PMD_INDEX_SIZE_L4_64K_4_6 5
#define PUD_INDEX_SIZE_L4_64K_4_6 5
+#define PMD_INDEX_SIZE_L4_64K_4_12 10
+#define PUD_INDEX_SIZE_L4_64K_4_12 7
+#define PGD_INDEX_SIZE_L4_64K_4_12 8
#define PTE_INDEX_SIZE_RADIX_64K 5
#define PMD_INDEX_SIZE_RADIX_64K 9
#define PUD_INDEX_SIZE_RADIX_64K 9
diff --git a/ppc64.c b/ppc64.c
index 84cec09..672ee60 100644
--- a/ppc64.c
+++ b/ppc64.c
@@ -447,10 +447,16 @@ ppc64_init(int when)
} else if (!(machdep->flags & BOOK3E) &&
(THIS_KERNEL_VERSION >= LINUX(4,6,0))) {
m->l1_index_size = PTE_INDEX_SIZE_L4_64K_3_10;
- m->l2_index_size = PMD_INDEX_SIZE_L4_64K_4_6;
- m->l3_index_size = PUD_INDEX_SIZE_L4_64K_4_6;
- m->l4_index_size = PGD_INDEX_SIZE_L4_64K_3_10;
+ if (THIS_KERNEL_VERSION >= LINUX(4,12,0)) {
+ m->l2_index_size = PMD_INDEX_SIZE_L4_64K_4_12;
+ m->l3_index_size = PUD_INDEX_SIZE_L4_64K_4_12;
+ m->l4_index_size = PGD_INDEX_SIZE_L4_64K_4_12;
+ } else {
+ m->l2_index_size = PMD_INDEX_SIZE_L4_64K_4_6;
+ m->l3_index_size = PUD_INDEX_SIZE_L4_64K_4_6;
+ m->l4_index_size = PGD_INDEX_SIZE_L4_64K_3_10;
+ }
} else if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) {
m->l1_index_size = PTE_INDEX_SIZE_L4_64K_3_10;
m->l2_index_size = PMD_INDEX_SIZE_L4_64K_3_10;
commit 03a3e57b9ad849314e262cac37787604a9fe8362
Author: Dave Anderson <anderson@redhat.com>
Date: Thu Nov 9 14:04:08 2017 -0500
Implemented a new "runq -T" option that displays the time lag of each
CPU relative to the most recent runqueue timestamp.
(oleksandr@redhat.com)
diff --git a/help.c b/help.c
index fa01bfb..e017b03 100644
--- a/help.c
+++ b/help.c
@@ -2532,7 +2532,7 @@ NULL
char *help_runq[] = {
"runq",
"run queue",
-"[-t] [-m] [-g] [-c cpu(s)]",
+"[-t] [-T] [-m] [-g] [-c cpu(s)]",
" With no argument, this command displays the tasks on the run queues",
" of each cpu.",
" ",
@@ -2541,6 +2541,8 @@ char *help_runq[] = {
" whichever applies; following each cpu timestamp is the last_run or ",
" timestamp value of the active task on that cpu, whichever applies, ",
" along with the task identification.",
+" -T Display the time lag of each CPU relative to the most recent runqueue",
+" timestamp.",
" -m Display the amount of time that the active task on each cpu has been",
" running, expressed in a format consisting of days, hours, minutes, ",
" seconds and milliseconds.",
diff --git a/task.c b/task.c
index f2628b7..724532d 100644
--- a/task.c
+++ b/task.c
@@ -55,6 +55,7 @@ static long rq_idx(int);
static long cpu_idx(int);
static void dump_runq(void);
static void dump_on_rq_timestamp(void);
+static void dump_on_rq_lag(void);
static void dump_on_rq_milliseconds(void);
static void dump_runqueues(void);
static void dump_prio_array(int, ulong, char *);
@@ -8045,10 +8046,11 @@ cmd_runq(void)
ulong *cpus = NULL;
int sched_debug = 0;
int dump_timestamp_flag = 0;
+ int dump_lag_flag = 0;
int dump_task_group_flag = 0;
int dump_milliseconds_flag = 0;
- while ((c = getopt(argcnt, args, "dtgmc:")) != EOF) {
+ while ((c = getopt(argcnt, args, "dtTgmc:")) != EOF) {
switch(c)
{
case 'd':
@@ -8057,6 +8059,9 @@ cmd_runq(void)
case 't':
dump_timestamp_flag = 1;
break;
+ case 'T':
+ dump_lag_flag = 1;
+ break;
case 'm':
dump_milliseconds_flag = 1;
break;
@@ -8092,6 +8097,8 @@ cmd_runq(void)
if (dump_timestamp_flag)
dump_on_rq_timestamp();
+ else if (dump_lag_flag)
+ dump_on_rq_lag();
else if (dump_milliseconds_flag)
dump_on_rq_milliseconds();
else if (sched_debug)
@@ -8177,6 +8184,90 @@ dump_on_rq_timestamp(void)
}
/*
+ * Runqueue timestamp struct for dump_on_rq_lag().
+ */
+struct runq_ts_info {
+ int cpu;
+ ulonglong ts;
+};
+
+/*
+ * Comparison function for dump_on_rq_lag().
+ * Sorts runqueue timestamps in a descending order.
+ */
+static int
+compare_runq_ts(const void *p1, const void *p2)
+{
+ const struct runq_ts_info *ts1 = p1;
+ const struct runq_ts_info *ts2 = p2;
+
+ if (ts1->ts > ts2->ts)
+ return -1;
+
+ if (ts1->ts < ts2->ts)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Calculates integer log10
+ */
+static ulong
+__log10ul(ulong x)
+{
+ ulong ret = 1;
+
+ while (x > 9) {
+ ret++;
+ x /= 10;
+ }
+
+ return ret;
+}
+
+/*
+ * Displays relative CPU lag.
+ */
+static void
+dump_on_rq_lag(void)
+{
+ struct syment *rq_sp;
+ int cpu;
+ ulong runq;
+ ulonglong timestamp;
+ struct runq_ts_info runq_ts[kt->cpus];
+
+ if (!(rq_sp = per_cpu_symbol_search("per_cpu__runqueues")))
+ error(FATAL, "per-cpu runqueues do not exist\n");
+ if (INVALID_MEMBER(rq_timestamp))
+ option_not_supported('T');
+
+ for (cpu = 0; cpu < kt->cpus; cpu++) {
+ if ((kt->flags & SMP) && (kt->flags &PER_CPU_OFF))
+ runq = rq_sp->value + kt->__per_cpu_offset[cpu];
+ else
+ runq = rq_sp->value;
+
+ readmem(runq + OFFSET(rq_timestamp), KVADDR, ×tamp,
+ sizeof(ulonglong), "per-cpu rq timestamp",
+ FAULT_ON_ERROR);
+
+ runq_ts[cpu].cpu = cpu;
+ runq_ts[cpu].ts = timestamp;
+ }
+
+ qsort(runq_ts, (size_t)kt->cpus, sizeof(struct runq_ts_info), compare_runq_ts);
+
+ for (cpu = 0; cpu < kt->cpus; cpu++) {
+ fprintf(fp, "%sCPU %d: %.2lf secs\n",
+ space(2 + __log10ul(kt->cpus) - __log10ul(runq_ts[cpu].cpu)),
+ runq_ts[cpu].cpu,
+ ((double)runq_ts[0].ts - (double)runq_ts[cpu].ts) / 1000000000.0);
+ }
+}
+
+/*
* Displays the runqueue and active task timestamps of each cpu.
*/
static void