Discussion:
Allow top(1) to search arguments
Edd Barrett
2016-02-10 23:04:18 UTC
Permalink
Hey,

I'd like top(1)'s filter feature (-g) to search process arguments. This
would make searching for (e.g.) Python scripts by name much easier. The
current behaviour only searches the program name, which for scripts is
the interpreter binary name (e.g. python2.7). Currently the best you
could do to find a Python script by name is to filter for "python", then
press "C" to enable argument display, then scan the args by eyeball.

Here is a diff that allows searching of arguments, but only if
arguments are currently being displayed.

I know the timing is bad, but any comments?


Index: machine.c
===================================================================
RCS file: /home/edd/cvsync/src/usr.bin/top/machine.c,v
retrieving revision 1.85
diff -u -p -r1.85 machine.c
--- machine.c 20 Aug 2015 22:32:42 -0000 1.85
+++ machine.c 6 Feb 2016 15:12:42 -0000
@@ -57,6 +57,8 @@
static int swapmode(int *, int *);
static char *state_abbr(struct kinfo_proc *);
static char *format_comm(struct kinfo_proc *);
+int cmd_matches(struct kinfo_proc *proc, char *cmd);
+static char **get_proc_args(struct kinfo_proc *kp);

/* get_process_info passes back a handle. This is what it looks like: */

@@ -360,6 +362,54 @@ getprocs(int op, int arg, int *cnt)
return (procbase);
}

+static char **
+get_proc_args(struct kinfo_proc *kp)
+{
+ static char **s;
+ size_t siz = 100;
+ int mib[4];
+
+ for (;; siz *= 2) {
+ if ((s = realloc(s, siz)) == NULL)
+ err(1, NULL);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC_ARGS;
+ mib[2] = kp->p_pid;
+ mib[3] = KERN_PROC_ARGV;
+ if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
+ break;
+ if (errno != ENOMEM)
+ return (NULL);
+ }
+ return (s);
+}
+
+int
+cmd_matches(struct kinfo_proc *proc, char *term)
+{
+ extern int show_args;
+ char **args = NULL, **arg = NULL;
+
+ if (!term) {
+ /* No command filter set */
+ return (1);
+ } else {
+ /* Filter set, process name needs to contain term */
+ if (strstr(proc->p_comm, term)) {
+ return (1);
+ }
+ /* If showing arguments, search those as well */
+ if (show_args) {
+ args = get_proc_args(proc);
+ for (arg = args; *arg != NULL; arg++) {
+ if (strstr(*arg, term))
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
caddr_t
get_process_info(struct system_info *si, struct process_select *sel,
int (*compare) (const void *, const void *))
@@ -421,8 +471,7 @@ get_process_info(struct system_info *si,
(!hide_uid || pp->p_ruid != sel->huid) &&
(!show_uid || pp->p_ruid == sel->uid) &&
(!show_pid || pp->p_pid == sel->pid) &&
- (!show_cmd || strstr(pp->p_comm,
- sel->command))) {
+ (!show_cmd || cmd_matches(pp, sel->command))) {
*prefp++ = pp;
active_procs++;
}
@@ -462,27 +511,17 @@ state_abbr(struct kinfo_proc *pp)
static char *
format_comm(struct kinfo_proc *kp)
{
- static char **s, buf[MAX_COLS];
- size_t siz = 100;
- char **p;
- int mib[4];
+ static char buf[MAX_COLS];
+ char **p, **s;
extern int show_args;

if (!show_args)
return (kp->p_comm);

- for (;; siz *= 2) {
- if ((s = realloc(s, siz)) == NULL)
- err(1, NULL);
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC_ARGS;
- mib[2] = kp->p_pid;
- mib[3] = KERN_PROC_ARGV;
- if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
- break;
- if (errno != ENOMEM)
- return (kp->p_comm);
- }
+ s = get_proc_args(kp);
+ if (s == NULL)
+ return (kp->p_comm);
+
buf[0] = '\0';
for (p = s; *p != NULL; p++) {
if (p != s)
Index: top.1
===================================================================
RCS file: /home/edd/cvsync/src/usr.bin/top/top.1,v
retrieving revision 1.66
diff -u -p -r1.66 top.1
--- top.1 6 May 2015 07:53:29 -0000 1.66
+++ top.1 6 Feb 2016 15:03:50 -0000
@@ -107,7 +107,8 @@ The default is 1 for dumb terminals.
.It Fl g Ar string
Display only processes that contain
.Ar string
-in their command name.
+in their command name. If displaying of arguments is enabled, the
+arguments are searched too.
.It Fl H
Show process threads in the display.
Normally, only the main process is shown.
@@ -305,7 +306,8 @@ command.
.It g Ar string
Display only processes that contain
.Ar string
-in their command name.
+in their command name. If displaying of arguments is enabled, the
+arguments are searched too.
.Sq g+
shows all processes.
.It H
--
Best Regards
Edd Barrett

http://www.theunixzoo.co.uk
patrick keshishian
2016-02-11 00:27:38 UTC
Permalink
Post by Edd Barrett
Hey,
I'd like top(1)'s filter feature (-g) to search process arguments. This
would make searching for (e.g.) Python scripts by name much easier. The
current behaviour only searches the program name, which for scripts is
the interpreter binary name (e.g. python2.7). Currently the best you
could do to find a Python script by name is to filter for "python", then
press "C" to enable argument display, then scan the args by eyeball.
Here is a diff that allows searching of arguments, but only if
arguments are currently being displayed.
I know the timing is bad, but any comments?
Index: machine.c
===================================================================
RCS file: /home/edd/cvsync/src/usr.bin/top/machine.c,v
retrieving revision 1.85
diff -u -p -r1.85 machine.c
--- machine.c 20 Aug 2015 22:32:42 -0000 1.85
+++ machine.c 6 Feb 2016 15:12:42 -0000
@@ -57,6 +57,8 @@
static int swapmode(int *, int *);
static char *state_abbr(struct kinfo_proc *);
static char *format_comm(struct kinfo_proc *);
+int cmd_matches(struct kinfo_proc *proc, char *cmd);
+static char **get_proc_args(struct kinfo_proc *kp);
/* get_process_info passes back a handle. This is what it looks like: */
@@ -360,6 +362,54 @@ getprocs(int op, int arg, int *cnt)
return (procbase);
}
+static char **
+get_proc_args(struct kinfo_proc *kp)
+{
+ static char **s;
+ size_t siz = 100;
+ int mib[4];
+
+ for (;; siz *= 2) {
+ if ((s = realloc(s, siz)) == NULL)
+ err(1, NULL);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC_ARGS;
+ mib[2] = kp->p_pid;
+ mib[3] = KERN_PROC_ARGV;
+ if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
+ break;
+ if (errno != ENOMEM)
+ return (NULL);
+ }
+ return (s);
+}
+
+int
+cmd_matches(struct kinfo_proc *proc, char *term)
+{
+ extern int show_args;
+ char **args = NULL, **arg = NULL;
+
+ if (!term) {
+ /* No command filter set */
+ return (1);
+ } else {
+ /* Filter set, process name needs to contain term */
+ if (strstr(proc->p_comm, term)) {
+ return (1);
+ }
+ /* If showing arguments, search those as well */
+ if (show_args) {
+ args = get_proc_args(proc);
+ for (arg = args; *arg != NULL; arg++) {
^^^^
NULL pointer dereference is possible here.

Also, unclear why you need both arg and args variables.

--patrick
Post by Edd Barrett
+ if (strstr(*arg, term))
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
caddr_t
get_process_info(struct system_info *si, struct process_select *sel,
int (*compare) (const void *, const void *))
@@ -421,8 +471,7 @@ get_process_info(struct system_info *si,
(!hide_uid || pp->p_ruid != sel->huid) &&
(!show_uid || pp->p_ruid == sel->uid) &&
(!show_pid || pp->p_pid == sel->pid) &&
- (!show_cmd || strstr(pp->p_comm,
- sel->command))) {
+ (!show_cmd || cmd_matches(pp, sel->command))) {
*prefp++ = pp;
active_procs++;
}
@@ -462,27 +511,17 @@ state_abbr(struct kinfo_proc *pp)
static char *
format_comm(struct kinfo_proc *kp)
{
- static char **s, buf[MAX_COLS];
- size_t siz = 100;
- char **p;
- int mib[4];
+ static char buf[MAX_COLS];
+ char **p, **s;
extern int show_args;
if (!show_args)
return (kp->p_comm);
- for (;; siz *= 2) {
- if ((s = realloc(s, siz)) == NULL)
- err(1, NULL);
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC_ARGS;
- mib[2] = kp->p_pid;
- mib[3] = KERN_PROC_ARGV;
- if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
- break;
- if (errno != ENOMEM)
- return (kp->p_comm);
- }
+ s = get_proc_args(kp);
+ if (s == NULL)
+ return (kp->p_comm);
+
buf[0] = '\0';
for (p = s; *p != NULL; p++) {
if (p != s)
Index: top.1
===================================================================
RCS file: /home/edd/cvsync/src/usr.bin/top/top.1,v
retrieving revision 1.66
diff -u -p -r1.66 top.1
--- top.1 6 May 2015 07:53:29 -0000 1.66
+++ top.1 6 Feb 2016 15:03:50 -0000
@@ -107,7 +107,8 @@ The default is 1 for dumb terminals.
.It Fl g Ar string
Display only processes that contain
.Ar string
-in their command name.
+in their command name. If displaying of arguments is enabled, the
+arguments are searched too.
.It Fl H
Show process threads in the display.
Normally, only the main process is shown.
@@ -305,7 +306,8 @@ command.
.It g Ar string
Display only processes that contain
.Ar string
-in their command name.
+in their command name. If displaying of arguments is enabled, the
+arguments are searched too.
.Sq g+
shows all processes.
.It H
--
Best Regards
Edd Barrett
http://www.theunixzoo.co.uk
Edd Barrett
2016-02-12 18:59:02 UTC
Permalink
Hey,

Thanks all for the comments.
Post by patrick keshishian
NULL pointer dereference is possible here.
Also, unclear why you need both arg and args variables.
^ Fix these and make cmd_matches() static.

Updated diff:


Index: machine.c
===================================================================
RCS file: /home/edd/cvsync/src/usr.bin/top/machine.c,v
retrieving revision 1.85
diff -u -p -r1.85 machine.c
--- machine.c 20 Aug 2015 22:32:42 -0000 1.85
+++ machine.c 12 Feb 2016 18:52:31 -0000
@@ -57,6 +57,8 @@
static int swapmode(int *, int *);
static char *state_abbr(struct kinfo_proc *);
static char *format_comm(struct kinfo_proc *);
+static int cmd_matches(struct kinfo_proc *proc, char *cmd);
+static char **get_proc_args(struct kinfo_proc *kp);

/* get_process_info passes back a handle. This is what it looks like: */

@@ -360,6 +362,61 @@ getprocs(int op, int arg, int *cnt)
return (procbase);
}

+static char **
+get_proc_args(struct kinfo_proc *kp)
+{
+ static char **s;
+ size_t siz = 100;
+ int mib[4];
+
+ for (;; siz *= 2) {
+ if ((s = realloc(s, siz)) == NULL)
+ err(1, NULL);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC_ARGS;
+ mib[2] = kp->p_pid;
+ mib[3] = KERN_PROC_ARGV;
+ if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
+ break;
+ if (errno != ENOMEM)
+ return (NULL);
+ }
+ return (s);
+}
+
+static int
+cmd_matches(struct kinfo_proc *proc, char *term)
+{
+ extern int show_args;
+ char **args = NULL;
+
+ if (!term) {
+ /* No command filter set */
+ return (1);
+ } else {
+ /* Filter set, process name needs to contain term */
+ if (strstr(proc->p_comm, term)) {
+ return (1);
+ }
+ /* If showing arguments, search those as well */
+ if (show_args) {
+ args = get_proc_args(proc);
+
+ if (args == NULL) {
+ /* Failed to get args, so can't search them */
+ return (0);
+ }
+
+ while (*args != NULL) {
+ if (strstr(*args, term))
+ return (1);
+ args++;
+ }
+ }
+ }
+ return (0);
+}
+
caddr_t
get_process_info(struct system_info *si, struct process_select *sel,
int (*compare) (const void *, const void *))
@@ -421,8 +478,7 @@ get_process_info(struct system_info *si,
(!hide_uid || pp->p_ruid != sel->huid) &&
(!show_uid || pp->p_ruid == sel->uid) &&
(!show_pid || pp->p_pid == sel->pid) &&
- (!show_cmd || strstr(pp->p_comm,
- sel->command))) {
+ (!show_cmd || cmd_matches(pp, sel->command))) {
*prefp++ = pp;
active_procs++;
}
@@ -462,27 +518,17 @@ state_abbr(struct kinfo_proc *pp)
static char *
format_comm(struct kinfo_proc *kp)
{
- static char **s, buf[MAX_COLS];
- size_t siz = 100;
- char **p;
- int mib[4];
+ static char buf[MAX_COLS];
+ char **p, **s;
extern int show_args;

if (!show_args)
return (kp->p_comm);

- for (;; siz *= 2) {
- if ((s = realloc(s, siz)) == NULL)
- err(1, NULL);
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC_ARGS;
- mib[2] = kp->p_pid;
- mib[3] = KERN_PROC_ARGV;
- if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
- break;
- if (errno != ENOMEM)
- return (kp->p_comm);
- }
+ s = get_proc_args(kp);
+ if (s == NULL)
+ return (kp->p_comm);
+
buf[0] = '\0';
for (p = s; *p != NULL; p++) {
if (p != s)
Index: top.1
===================================================================
RCS file: /home/edd/cvsync/src/usr.bin/top/top.1,v
retrieving revision 1.66
diff -u -p -r1.66 top.1
--- top.1 6 May 2015 07:53:29 -0000 1.66
+++ top.1 6 Feb 2016 15:03:50 -0000
@@ -107,7 +107,8 @@ The default is 1 for dumb terminals.
.It Fl g Ar string
Display only processes that contain
.Ar string
-in their command name.
+in their command name. If displaying of arguments is enabled, the
+arguments are searched too.
.It Fl H
Show process threads in the display.
Normally, only the main process is shown.
@@ -305,7 +306,8 @@ command.
.It g Ar string
Display only processes that contain
.Ar string
-in their command name.
+in their command name. If displaying of arguments is enabled, the
+arguments are searched too.
.Sq g+
shows all processes.
.It H
--
Best Regards
Edd Barrett

http://www.theunixzoo.co.uk
Loading...