From nobody Fri Oct 3 11:22:54 2025 Received: from mxhk.zte.com.cn (mxhk.zte.com.cn [160.30.148.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0D0316FC3; Tue, 2 Sep 2025 02:01:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=160.30.148.35 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756778482; cv=none; b=TEypCF/q5VoFNLKNf2CWdUyND/IenC80ip7xvj+re2OKzEE+fzLbY8tYpI+pbBkFmab6qUwfbpHTcfXIZmmZ5JnZbO/WVKeCzFAuJ/2gbvI7LY+Y0D9Y8B/LQS0bS/uRIKTOKwHAYZ3+IQfx62CgkX/Pokj+6yU1GNnwqopIUdM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756778482; c=relaxed/simple; bh=euhcNSxCNUd3eB/FwSOi1w9wqoBfz3GgiBLMXntU10c=; h=Date:Message-ID:In-Reply-To:References:Mime-Version:From:To:Cc: Subject:Content-Type; b=KIx/QuwO9MfmrmFUnocy9yGMyqER2Dau9PllPkayEPHxDXnaS0TUbUOF7+19ELRRp9sLfMPBV64ZJXfcAAdLgTdNQgFtAeh/h74c4RAhUFOlJ5gFjmyVjc5YK5F5m8+AZqez7ViFw7/zRqfWMA9IpCHNI2yZzE93uCUU8wB0buo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn; spf=pass smtp.mailfrom=zte.com.cn; arc=none smtp.client-ip=160.30.148.35 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zte.com.cn Received: from mse-fl2.zte.com.cn (unknown [10.5.228.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxhk.zte.com.cn (FangMail) with ESMTPS id 4cG89R15cMz8Xs76; Tue, 02 Sep 2025 10:01:15 +0800 (CST) Received: from xaxapp05.zte.com.cn ([10.99.98.109]) by mse-fl2.zte.com.cn with SMTP id 58220Udv031365; Tue, 2 Sep 2025 10:00:30 +0800 (+08) (envelope-from fan.yu9@zte.com.cn) Received: from mapi (xaxapp04[null]) by mapi (Zmail) with MAPI id mid32; Tue, 2 Sep 2025 10:00:30 +0800 (CST) Date: Tue, 2 Sep 2025 10:00:30 +0800 (CST) X-Zmail-TransId: 2afb68b64fbe9e0-685a9 X-Mailer: Zmail v1.0 Message-ID: <20250902100030967nPEcUoRRSnruExakQxAIm@zte.com.cn> In-Reply-To: <202509020957458514CMgUiaqPjTURNET_d-w0@zte.com.cn> References: 202509020957458514CMgUiaqPjTURNET_d-w0@zte.com.cn Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: To: , Cc: , , , , Subject: =?UTF-8?B?W1BBVENIIGxpbnV4LW5leHQgMS8zXSB0b29scy9kZWxheXRvcDogYWRkIG1lbW9yeSB2ZXJib3NlIG1vZGUgc3VwcG9ydA==?= X-MAIL: mse-fl2.zte.com.cn 58220Udv031365 X-TLS: YES X-SPF-DOMAIN: zte.com.cn X-ENVELOPE-SENDER: fan.yu9@zte.com.cn X-SPF: None X-SOURCE-IP: 10.5.228.133 unknown Tue, 02 Sep 2025 10:01:15 +0800 X-Fangmail-Anti-Spam-Filtered: true X-Fangmail-MID-QID: 68B64FEB.000/4cG89R15cMz8Xs76 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Fan Yu The original delaytop tool always displayed detailed memory subsystem breakdown, which could be overwhelming for users who only need high-level overview. Add flexible display control allowing users to choose their preferred information granularity. The new flexibility provides: 1) For quick monitoring: use normal mode to reduce visual clutter 2) For deep analysis: use verbose mode to see all memory subsystem details Signed-off-by: Fan Yu Acked-by: Yang Yang --- tools/accounting/delaytop.c | 111 ++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/tools/accounting/delaytop.c b/tools/accounting/delaytop.c index 9afb1ffc00ba..f1e2e1cca4b8 100644 --- a/tools/accounting/delaytop.c +++ b/tools/accounting/delaytop.c @@ -68,6 +68,8 @@ ret >=3D 0; \ }) #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" +#define FMT_NORMAL "%8.2f %8.2f %8.2f %8.2f\n" +#define FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f %8.2f %8.2f %8= .2f\n" /* Program settings structure */ struct config { @@ -78,6 +80,7 @@ struct config { int output_one_time; /* Output once and exit */ int monitor_pid; /* Monitor specific PID */ char *container_path; /* Path to container cgroup */ + int mem_verbose_mode; /* Memory detailed display mode */ }; /* PSI statistics structure */ @@ -163,13 +166,14 @@ static void usage(void) { printf("Usage: delaytop [Options]\n" "Options:\n" - " -h, --help Show this help message and exit\n" - " -d, --delay=3DSECONDS Set refresh interval (default: 2 seconds, min:= 1)\n" - " -n, --iterations=3DCOUNT Set number of updates (default: 0 =3D infinit= e)\n" - " -P, --processes=3DNUMBER Set maximum number of processes to show (defa= ult: 20, max: 1000)\n" - " -o, --once Display once and exit\n" - " -p, --pid=3DPID Monitor only the specified PID\n" - " -C, --container=3DPATH Monitor the container at specified cgroup path= \n"); + " -h, --help Show this help message and exit\n" + " -d, --delay=3DSECONDS Set refresh interval (default: 2 seconds, m= in: 1)\n" + " -n, --iterations=3DCOUNT Set number of updates (default: 0 =3D infin= ite)\n" + " -P, --processes=3DNUMBER Set maximum number of processes to show (de= fault: 20, max: 1000)\n" + " -o, --once Display once and exit\n" + " -p, --pid=3DPID Monitor only the specified PID\n" + " -C, --container=3DPATH Monitor the container at specified cgroup p= ath\n" + " -M, --memverbose Display memory detailed information\n"); exit(0); } @@ -185,6 +189,7 @@ static void parse_args(int argc, char **argv) {"once", no_argument, 0, 'o'}, {"processes", required_argument, 0, 'P'}, {"container", required_argument, 0, 'C'}, + {"memverbose", no_argument, 0, 'M'}, {0, 0, 0, 0} }; @@ -196,11 +201,12 @@ static void parse_args(int argc, char **argv) cfg.output_one_time =3D 0; cfg.monitor_pid =3D 0; /* 0 means monitor all PIDs */ cfg.container_path =3D NULL; + cfg.mem_verbose_mode =3D 0; while (1) { int option_index =3D 0; - c =3D getopt_long(argc, argv, "hd:n:p:oP:C:", long_options, &option_inde= x); + c =3D getopt_long(argc, argv, "hd:n:p:oP:C:M", long_options, &option_ind= ex); if (c =3D=3D -1) break; @@ -247,6 +253,9 @@ static void parse_args(int argc, char **argv) case 'C': cfg.container_path =3D strdup(optarg); break; + case 'M': + cfg.mem_verbose_mode =3D 1; + break; default: fprintf(stderr, "Try 'delaytop --help' for more information.\n"); exit(1); @@ -582,6 +591,25 @@ static double average_ms(unsigned long long total, uns= igned long long count) return (double)total / 1000000.0 / count; } +/* Calculate average delay in milliseconds for memory */ +static unsigned long long task_total_mem_delay(const struct task_info *t) +{ + return t->swapin_delay_total + + t->freepages_delay_total + + t->thrashing_delay_total + + t->compact_delay_total + + t->wpcopy_delay_total; +} + +static unsigned long long task_total_mem_count(const struct task_info *t) +{ + return t->swapin_count + + t->freepages_count + + t->thrashing_count + + t->compact_count + + t->wpcopy_count; +} + /* Comparison function for sorting tasks */ static int compare_tasks(const void *a, const void *b) { @@ -740,27 +768,62 @@ static void display_results(void) } suc &=3D BOOL_FPRINT(out, "Top %d processes (sorted by CPU delay):\n", cfg.max_processes); - suc &=3D BOOL_FPRINT(out, "%5s %5s %-17s", "PID", "TGID", "COMMAND"); - suc &=3D BOOL_FPRINT(out, "%7s %7s %7s %7s %7s %7s %7s %7s\n", - "CPU(ms)", "IO(ms)", "SWAP(ms)", "RCL(ms)", - "THR(ms)", "CMP(ms)", "WP(ms)", "IRQ(ms)"); + suc &=3D BOOL_FPRINT(out, "%8s %8s %-17s", "PID", "TGID", "COMMAND"); + + if (!cfg.mem_verbose_mode) { + suc &=3D BOOL_FPRINT(out, "%8s %8s %8s %8s\n", + "CPU(ms)", "IO(ms)", "IRQ(ms)", "MEM(ms)"); + suc &=3D BOOL_FPRINT(out, "-----------------------"); + suc &=3D BOOL_FPRINT(out, "-----------------------"); + suc &=3D BOOL_FPRINT(out, "--------------------------\n"); + } else { + suc &=3D BOOL_FPRINT(out, "%8s %8s %8s %8s %8s %8s %8s %8s %8s\n", + "CPU(ms)", "IO(ms)", "IRQ(ms)", "MEM(ms)", + "SWAP(ms)", "RCL(ms)", "THR(ms)", "CMP(ms)", "WP(ms)"); + suc &=3D BOOL_FPRINT(out, "-----------------------"); + suc &=3D BOOL_FPRINT(out, "-----------------------"); + suc &=3D BOOL_FPRINT(out, "-----------------------"); + suc &=3D BOOL_FPRINT(out, "-----------------------"); + suc &=3D BOOL_FPRINT(out, "-------------------------\n"); + } + - suc &=3D BOOL_FPRINT(out, "----------------------------------------------= -"); - suc &=3D BOOL_FPRINT(out, "----------------------------------------------= \n"); count =3D task_count < cfg.max_processes ? task_count : cfg.max_processes; for (i =3D 0; i < count; i++) { - suc &=3D BOOL_FPRINT(out, "%5d %5d %-15s", + suc &=3D BOOL_FPRINT(out, "%8d %8d %-15s", tasks[i].pid, tasks[i].tgid, tasks[i].command); - suc &=3D BOOL_FPRINT(out, "%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.= 2f\n", - average_ms(tasks[i].cpu_delay_total, tasks[i].cpu_count), - average_ms(tasks[i].blkio_delay_total, tasks[i].blkio_count), - average_ms(tasks[i].swapin_delay_total, tasks[i].swapin_count), - average_ms(tasks[i].freepages_delay_total, tasks[i].freepages_count), - average_ms(tasks[i].thrashing_delay_total, tasks[i].thrashing_count), - average_ms(tasks[i].compact_delay_total, tasks[i].compact_count), - average_ms(tasks[i].wpcopy_delay_total, tasks[i].wpcopy_count), - average_ms(tasks[i].irq_delay_total, tasks[i].irq_count)); + if (!cfg.mem_verbose_mode) { + suc &=3D BOOL_FPRINT(out, FMT_NORMAL, + average_ms(tasks[i].cpu_delay_total, + tasks[i].cpu_count), + average_ms(tasks[i].blkio_delay_total, + tasks[i].blkio_count), + average_ms(tasks[i].irq_delay_total, + tasks[i].irq_count), + average_ms(task_total_mem_delay(&tasks[i]), + task_total_mem_count(&tasks[i]))); + } else { + suc &=3D BOOL_FPRINT(out, FMT_MEMVERBOSE, + average_ms(tasks[i].cpu_delay_total, + tasks[i].cpu_count), + average_ms(tasks[i].blkio_delay_total, + tasks[i].blkio_count), + average_ms(tasks[i].irq_delay_total, + tasks[i].irq_count), + average_ms(task_total_mem_delay(&tasks[i]), + task_total_mem_count(&tasks[i])), + average_ms(tasks[i].swapin_delay_total, + tasks[i].swapin_count), + average_ms(tasks[i].freepages_delay_total, + tasks[i].freepages_count), + average_ms(tasks[i].thrashing_delay_total, + tasks[i].thrashing_count), + average_ms(tasks[i].compact_delay_total, + tasks[i].compact_count), + average_ms(tasks[i].wpcopy_delay_total, + tasks[i].wpcopy_count)); + } } suc &=3D BOOL_FPRINT(out, "\n"); --=20 2.25.1 From nobody Fri Oct 3 11:22:54 2025 Received: from mxhk.zte.com.cn (mxhk.zte.com.cn [160.30.148.34]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76D2F6FC3; Tue, 2 Sep 2025 02:04:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=160.30.148.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756778654; cv=none; b=Si/j9gwbCkCjrPhp/Kl8p5SPDHGpP6tactMwDail4OIBOl/gKgS8LJUwLSOtaHagT9wCIkTAMqpnkQq5JxrCZ5r41OFEwfJL0BtNIivJbfXNIcUabPsNDxiiP2x7JkuFyUTE5afz1lcNouDZgrEGCjdbIEkyiVMgBAk1F6scTBM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756778654; c=relaxed/simple; bh=yVkDJCBoZhjmkInVYNZZwsUydbWhTQzviNY/ujQT5r4=; h=Date:Message-ID:In-Reply-To:References:Mime-Version:From:To:Cc: Subject:Content-Type; b=R8UblIVWuhd4g8XLK2h/gONNRVW/D+MfEt976kWrQWtI0OLhSRqgbOOizGNIAaV9G1z/aNCEjO6jNWegDy6bTWvpQ4EInG25Ph7F2+a1lpIrV2mS+qm/VvoKPQU8AcCB/yjbKAfbzXKORaYsGW1bqQsSQI6IDuN1NQ6jPBbO+n8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn; spf=pass smtp.mailfrom=zte.com.cn; arc=none smtp.client-ip=160.30.148.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zte.com.cn Received: from mse-fl1.zte.com.cn (unknown [10.5.228.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxhk.zte.com.cn (FangMail) with ESMTPS id 4cG8Dn0VVHz5PM3D; Tue, 02 Sep 2025 10:04:09 +0800 (CST) Received: from xaxapp05.zte.com.cn ([10.99.98.109]) by mse-fl1.zte.com.cn with SMTP id 58223qi4051390; Tue, 2 Sep 2025 10:03:52 +0800 (+08) (envelope-from fan.yu9@zte.com.cn) Received: from mapi (xaxapp01[null]) by mapi (Zmail) with MAPI id mid32; Tue, 2 Sep 2025 10:03:53 +0800 (CST) Date: Tue, 2 Sep 2025 10:03:53 +0800 (CST) X-Zmail-TransId: 2af968b65089775-7732f X-Mailer: Zmail v1.0 Message-ID: <20250902100353835xyAecL45pVFk1sbaC16f4@zte.com.cn> In-Reply-To: <202509020957458514CMgUiaqPjTURNET_d-w0@zte.com.cn> References: 202509020957458514CMgUiaqPjTURNET_d-w0@zte.com.cn Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: To: , Cc: , , , , , Subject: =?UTF-8?B?W1BBVENIIGxpbnV4LW5leHQgMi8zXSB0b29scy9kZWxheXRvcDogYWRkIGZsZXhpYmxlIHNvcnRpbmcgYnkgZGVsYXkgZmllbGQ=?= X-MAIL: mse-fl1.zte.com.cn 58223qi4051390 X-TLS: YES X-SPF-DOMAIN: zte.com.cn X-ENVELOPE-SENDER: fan.yu9@zte.com.cn X-SPF: None X-SOURCE-IP: 10.5.228.132 unknown Tue, 02 Sep 2025 10:04:09 +0800 X-Fangmail-Anti-Spam-Filtered: true X-Fangmail-MID-QID: 68B65099.000/4cG8Dn0VVHz5PM3D Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Fan Yu The delaytop tool only supported sorting by CPU delay, which limited its usefulness when users needed to identify bottlenecks in other subsystems. Users had no way to sort processes by IO, memory, or other delay types to quickly pinpoint specific performance issues. Add -s/--sort option to allow sorting by different delay types: 1) Basic modes: cpu, io, irq, mem =20 2) Detailed modes (-M required): swap, reclaim, thrashing, compact, wpcopy Users can now quickly identify bottlenecks in specific subsystems by sorting processes by the relevant delay metric. Signed-off-by: Fan Yu Acked-by: Yang Yang Reviewed-by: xu xin --- tools/accounting/delaytop.c | 130 +++++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 9 deletions(-) diff --git a/tools/accounting/delaytop.c b/tools/accounting/delaytop.c index f1e2e1cca4b8..39852cd70bdf 100644 --- a/tools/accounting/delaytop.c +++ b/tools/accounting/delaytop.c @@ -173,7 +173,9 @@ static void usage(void) " -o, --once Display once and exit\n" " -p, --pid=3DPID Monitor only the specified PID\n" " -C, --container=3DPATH Monitor the container at specified cgroup p= ath\n" - " -M, --memverbose Display memory detailed information\n"); + " -M, --memverbose Display memory detailed information\n" + " -s, --sort=3DFIELD Sort by delay field (default: cpu)\n" + " Types: cpu|io|irq|mem|swap|reclaim|thrashing|= compact|wpcopy\n"); exit(0); } @@ -188,6 +190,7 @@ static void parse_args(int argc, char **argv) {"pid", required_argument, 0, 'p'}, {"once", no_argument, 0, 'o'}, {"processes", required_argument, 0, 'P'}, + {"sort", required_argument, 0, 's'}, {"container", required_argument, 0, 'C'}, {"memverbose", no_argument, 0, 'M'}, {0, 0, 0, 0} @@ -206,7 +209,7 @@ static void parse_args(int argc, char **argv) while (1) { int option_index =3D 0; - c =3D getopt_long(argc, argv, "hd:n:p:oP:C:M", long_options, &option_ind= ex); + c =3D getopt_long(argc, argv, "hd:n:p:oP:C:Ms:", long_options, &option_i= ndex); if (c =3D=3D -1) break; @@ -256,11 +259,53 @@ static void parse_args(int argc, char **argv) case 'M': cfg.mem_verbose_mode =3D 1; break; + case 's': + if (strlen(optarg) =3D=3D 0) { + fprintf(stderr, "Error: empty sort field\n"); + exit(1); + } + + if (strncmp(optarg, "cpu", 3) =3D=3D 0) + cfg.sort_field =3D 'c'; + else if (strncmp(optarg, "io", 2) =3D=3D 0) + cfg.sort_field =3D 'i'; + else if (strncmp(optarg, "irq", 3) =3D=3D 0) + cfg.sort_field =3D 'q'; + else if (strncmp(optarg, "mem", 3) =3D=3D 0) + cfg.sort_field =3D 'm'; + else if (strncmp(optarg, "swap", 4) =3D=3D 0) + cfg.sort_field =3D 's'; + else if (strncmp(optarg, "reclaim", 7) =3D=3D 0) + cfg.sort_field =3D 'r'; + else if (strncmp(optarg, "thrashing", 9) =3D=3D 0) + cfg.sort_field =3D 't'; + else if (strncmp(optarg, "compact", 7) =3D=3D 0) + cfg.sort_field =3D 'p'; + else if (strncmp(optarg, "wpcopy", 7) =3D=3D 0) + cfg.sort_field =3D 'w'; + else { + fprintf(stderr, "Error: invalid sort field\n"); + fprintf(stderr, "Try to use cpu|io|irq|mem|"); + fprintf(stderr, "swap|reclaim|thrashing|compact|wpcopy\n"); + exit(1); + } + break; default: fprintf(stderr, "Try 'delaytop --help' for more information.\n"); exit(1); } } + + /* Validate sorting field compatibility with memory verbose mode */ + if (cfg.mem_verbose_mode =3D=3D 0 && + cfg.sort_field =3D=3D 's' || + cfg.sort_field =3D=3D 'r' || + cfg.sort_field =3D=3D 't' || + cfg.sort_field =3D=3D 'p' || + cfg.sort_field =3D=3D 'w') { + fprintf(stderr, "Error: mem verbose mode is off, try to use -M\n"); + exit(1); + } } /* Create a raw netlink socket and bind */ @@ -621,12 +666,77 @@ static int compare_tasks(const void *a, const void *b) case 'c': /* CPU */ avg1 =3D average_ms(t1->cpu_delay_total, t1->cpu_count); avg2 =3D average_ms(t2->cpu_delay_total, t2->cpu_count); - if (avg1 !=3D avg2) - return avg2 > avg1 ? 1 : -1; - return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; + break; + case 'i': /* IO */ + avg1 =3D average_ms(t1->blkio_delay_total, t1->blkio_count); + avg2 =3D average_ms(t2->blkio_delay_total, t2->blkio_count); + break; + case 'q': /* IRQ */ + avg1 =3D average_ms(t1->irq_delay_total, t1->irq_count); + avg2 =3D average_ms(t2->irq_delay_total, t2->irq_count); + break; + case 'm': /* MEM(total) */ + avg1 =3D average_ms(task_total_mem_delay(t1), task_total_mem_count(t1)); + avg2 =3D average_ms(task_total_mem_delay(t2), task_total_mem_count(t2)); + break; + /* Memory detailed display mode */ + case 's': /* swapin (SWAP) */ + avg1 =3D average_ms(t1->swapin_delay_total, t1->swapin_count); + avg2 =3D average_ms(t2->swapin_delay_total, t2->swapin_count); + break; + case 'r': /* freepages (RCL) */ + avg1 =3D average_ms(t1->freepages_delay_total, t1->freepages_count); + avg2 =3D average_ms(t2->freepages_delay_total, t2->freepages_count); + break; + case 't': /* thrashing (THR) */ + avg1 =3D average_ms(t1->thrashing_delay_total, t1->thrashing_count); + avg2 =3D average_ms(t2->thrashing_delay_total, t2->thrashing_count); + break; + case 'p': /* compact (CMP) */ + avg1 =3D average_ms(t1->compact_delay_total, t1->compact_count); + avg2 =3D average_ms(t2->compact_delay_total, t2->compact_count); + break; + case 'w': /* wpcopy (WP) */ + avg1 =3D average_ms(t1->wpcopy_delay_total, t1->wpcopy_count); + avg2 =3D average_ms(t2->wpcopy_delay_total, t2->wpcopy_count); + break; + default: + avg1 =3D average_ms(t1->cpu_delay_total, t1->cpu_count); + avg2 =3D average_ms(t2->cpu_delay_total, t2->cpu_count); + break; + } + + if (avg1 !=3D avg2) + return avg2 > avg1 ? 1 : -1; + + return 0; +} +static const char *get_sort_field(char sort_field) +{ + switch (sort_field) { + case 'c': + return "CPU"; + case 'i': + return "IO"; + case 'q': + return "IRQ"; + /* MEM(total) */ + case 'm': + return "MEM"; + /* Memory detailed display mode */ + case 's': + return "SWAP"; + case 'r': + return "RCL"; + case 't': + return "THR"; + case 'p': + return "CMP"; + case 'w': + return "WP"; default: - return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; + return "UNKNOWN"; /* handle error */ } } @@ -705,6 +815,7 @@ static void display_results(void) { time_t now =3D time(NULL); struct tm *tm_now =3D localtime(&now); + const char *sort_field; FILE *out =3D stdout; char timestamp[32]; bool suc =3D true; @@ -766,8 +877,10 @@ static void display_results(void) container_stats.nr_stopped, container_stats.nr_uninterruptible, container_stats.nr_io_wait); } - suc &=3D BOOL_FPRINT(out, "Top %d processes (sorted by CPU delay):\n", - cfg.max_processes); + + /* Task delay output */ + suc &=3D BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n", + cfg.max_processes, get_sort_field(cfg.sort_field)); suc &=3D BOOL_FPRINT(out, "%8s %8s %-17s", "PID", "TGID", "COMMAND"); if (!cfg.mem_verbose_mode) { @@ -787,7 +900,6 @@ static void display_results(void) suc &=3D BOOL_FPRINT(out, "-------------------------\n"); } - count =3D task_count < cfg.max_processes ? task_count : cfg.max_processes; for (i =3D 0; i < count; i++) { --=20 2.25.1 From nobody Fri Oct 3 11:22:54 2025 Received: from mxhk.zte.com.cn (mxhk.zte.com.cn [160.30.148.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 279616FC3; Tue, 2 Sep 2025 02:05:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=160.30.148.35 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756778741; cv=none; b=rtJqMXsAELpjd10tg2XW2+0EozarzuChBvPgc1J9AU2YqL6Dt3yT41Uduw7gQofS446RMse0Um90Auh8UbxgO2tCXII+Zgc50poJ9tOctQm+gZ49/HpocsAIQ8DqLzJXyCZ3w33Dh0U0ABWqX5+vbkCA4hGlqZdW7a0WtyYQpiU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756778741; c=relaxed/simple; bh=/wa/E2zW11MLhVaS2g/6my3y8dJxlfissL2isUxk2Uk=; h=Date:Message-ID:In-Reply-To:References:Mime-Version:From:To:Cc: Subject:Content-Type; b=blQjKtdcQA7dTniNhVgNK1exaYCCXET0ieKj5k/IWgxgCtLEQA+mVjz4F8wqD5dSqstk77dOTq9IYWtZ50jn71kY9sfZYmZb244DRKOOOMi1vxlKYDRQXayQdqyBVPgArMBAjK5xu3MqsA2QASMpg9syZ2LexwEKO/T70NwdibM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn; spf=pass smtp.mailfrom=zte.com.cn; arc=none smtp.client-ip=160.30.148.35 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zte.com.cn Received: from mse-fl1.zte.com.cn (unknown [10.5.228.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxhk.zte.com.cn (FangMail) with ESMTPS id 4cG8GR2dypz8Xs70; Tue, 02 Sep 2025 10:05:35 +0800 (CST) Received: from xaxapp05.zte.com.cn ([10.99.98.109]) by mse-fl1.zte.com.cn with SMTP id 582259Pk052719; Tue, 2 Sep 2025 10:05:09 +0800 (+08) (envelope-from fan.yu9@zte.com.cn) Received: from mapi (xaxapp04[null]) by mapi (Zmail) with MAPI id mid32; Tue, 2 Sep 2025 10:05:10 +0800 (CST) Date: Tue, 2 Sep 2025 10:05:10 +0800 (CST) X-Zmail-TransId: 2afb68b650d6b72-70733 X-Mailer: Zmail v1.0 Message-ID: <20250902100510629gbt-dwxQ09_35jcv_YO3U@zte.com.cn> In-Reply-To: <202509020957458514CMgUiaqPjTURNET_d-w0@zte.com.cn> References: 202509020957458514CMgUiaqPjTURNET_d-w0@zte.com.cn Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: To: , Cc: , , , , , Subject: =?UTF-8?B?W1BBVENIIGxpbnV4LW5leHQgMy8zXSB0b29scy9kZWxheXRvcDogYWRkIGludGVyYWN0aXZlIG1vZGUgd2l0aCBrZXlib2FyZCBjb250cm9scw==?= X-MAIL: mse-fl1.zte.com.cn 582259Pk052719 X-TLS: YES X-SPF-DOMAIN: zte.com.cn X-ENVELOPE-SENDER: fan.yu9@zte.com.cn X-SPF: None X-SOURCE-IP: 10.5.228.132 unknown Tue, 02 Sep 2025 10:05:35 +0800 X-Fangmail-Anti-Spam-Filtered: true X-Fangmail-MID-QID: 68B650EF.002/4cG8GR2dypz8Xs70 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Fan Yu The original delaytop only supported static output with limited interaction. Users had to restart the tool with different command-line options to change sorting or display modes, which disrupted continuous monitoring and reduced productivity during performance investigations. Adds real-time interactive controls through keyboard input: 1) Add interactive menu system with visual prompts 2) Support dynamic sorting changes without restarting 3) Enable toggle of memory verbose mode with 'M' key The interactive mode transforms delaytop from a static monitoring tool into a dynamic investigation platform, allowing users to adapt the view in real-time based on observed performance patterns. Signed-off-by: Fan Yu Acked-by: Yang Yang --- tools/accounting/delaytop.c | 112 ++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 30 deletions(-) diff --git a/tools/accounting/delaytop.c b/tools/accounting/delaytop.c index 39852cd70bdf..21fb215d2928 100644 --- a/tools/accounting/delaytop.c +++ b/tools/accounting/delaytop.c @@ -140,6 +140,7 @@ static struct task_info tasks[MAX_TASKS]; static int task_count; static int running =3D 1; static struct container_stats container_stats; +static int sort_selected; /* Netlink socket variables */ static int nl_sd =3D -1; @@ -878,6 +879,17 @@ static void display_results(void) container_stats.nr_io_wait); } + /* Interacive command */ + suc &=3D BOOL_FPRINT(out, "[o]sort [M]memverbose [q]quit\n"); + if (sort_selected) { + if (cfg.mem_verbose_mode) + suc &=3D BOOL_FPRINT(out, + "sort selection: [c]CPU [i]IO [s]SWAP [r]RCL [t]THR [p]CMP [w]WP [q]IRQ= \n"); + else + suc &=3D BOOL_FPRINT(out, + "sort selection: [c]CPU [i]IO [m]MEM [q]IRQ\n"); + } + /* Task delay output */ suc &=3D BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n", cfg.max_processes, get_sort_field(cfg.sort_field)); @@ -944,11 +956,73 @@ static void display_results(void) perror("Error writing to output"); } +/* Check for keyboard input with timeout based on cfg.delay */ +static char check_for_keypress(void) +{ + struct timeval tv =3D {cfg.delay, 0}; + fd_set readfds; + char ch =3D 0; + + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + int r =3D select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv); + + if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { + read(STDIN_FILENO, &ch, 1); + return ch; + } + + return 0; +} + +/* Handle keyboard input: sorting selection, mode toggle, or quit */ +static void handle_keypress(char ch, int *running) +{ + if (sort_selected) { + switch (ch) { + case 'c': + case 'i': + case 'q': + case 'm': + cfg.sort_field =3D ch; + break; + /* Only for memory verbose mode */ + case 's': + case 'r': + case 't': + case 'p': + case 'w': + if (cfg.mem_verbose_mode) + cfg.sort_field =3D ch; + break; + default: + break; + } + sort_selected =3D 0; + } else { + switch (ch) { + case 'o': + sort_selected =3D 1; + break; + case 'M': + cfg.mem_verbose_mode =3D !cfg.mem_verbose_mode; + cfg.sort_field =3D 'c'; /* Change to default sort mode */ + break; + case 'q': + case 'Q': + *running =3D 0; + break; + default: + break; + } + } +} + /* Main function */ int main(int argc, char **argv) { int iterations =3D 0; - int use_q_quit =3D 0; + char keypress; /* Parse command line arguments */ parse_args(argc, argv); @@ -968,12 +1042,8 @@ int main(int argc, char **argv) exit(1); } - if (!cfg.output_one_time) { - use_q_quit =3D 1; - enable_raw_mode(); - printf("Press 'q' to quit.\n"); - fflush(stdout); - } + /* Set terminal to non-canonical mode for interaction */ + enable_raw_mode(); /* Main loop */ while (running) { @@ -1001,32 +1071,14 @@ int main(int argc, char **argv) if (cfg.output_one_time) break; - /* Check for 'q' key to quit */ - if (use_q_quit) { - struct timeval tv =3D {cfg.delay, 0}; - fd_set readfds; - - FD_ZERO(&readfds); - FD_SET(STDIN_FILENO, &readfds); - int r =3D select(STDIN_FILENO+1, &readfds, NULL, NULL, &tv); - - if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { - char ch =3D 0; - - read(STDIN_FILENO, &ch, 1); - if (ch =3D=3D 'q' || ch =3D=3D 'Q') { - running =3D 0; - break; - } - } - } else { - sleep(cfg.delay); - } + /* Keypress for interactive usage */ + keypress =3D check_for_keypress(); + if (keypress) + handle_keypress(keypress, &running); } /* Restore terminal mode */ - if (use_q_quit) - disable_raw_mode(); + disable_raw_mode(); /* Cleanup */ close(nl_sd); --=20 2.25.1