1 /*        $NetBSD: db_proc.c,v 1.16 2024/04/15 06:48:06 skrll Exp $   */
2 
3 /*-
4  * Copyright (c) 2009, 2020 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1982, 1986, 1989, 1991, 1993
34  *        The Regents of the University of California.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. Neither the name of the University nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58  * SUCH DAMAGE.
59  *
60  *        from: kern_proc.c   8.4 (Berkeley) 1/4/94
61  */
62 
63 #include <sys/cdefs.h>
64 __KERNEL_RCSID(0, "$NetBSD: db_proc.c,v 1.16 2024/04/15 06:48:06 skrll Exp $");
65 
66 #ifndef _KERNEL
67 #include <stdbool.h>
68 #endif
69 
70 #include <sys/param.h>
71 #include <sys/cpu.h>
72 #include <sys/proc.h>
73 #ifdef _KERNEL      /* XXX */
74 #include <sys/kauth.h>
75 #endif
76 
77 #include <ddb/ddb.h>
78 
79 proc_t *
db_proc_first(void)80 db_proc_first(void)
81 {
82 
83           return db_read_ptr("allproc");
84 }
85 
86 proc_t *
db_proc_next(proc_t * p)87 db_proc_next(proc_t *p)
88 {
89 
90           db_read_bytes((db_addr_t)&p->p_list.le_next, sizeof(p), (char *)&p);
91           return p;
92 }
93 
94 proc_t *
db_proc_find(pid_t pid)95 db_proc_find(pid_t pid)
96 {
97           proc_t *p;
98           pid_t tp;
99 
100           for (p = db_proc_first(); p != NULL; p = db_proc_next(p)) {
101                     db_read_bytes((db_addr_t)&p->p_pid, sizeof(tp),
102                         (char *)&tp);
103                     if (tp == pid) {
104                               return p;
105                     }
106           }
107           return NULL;
108 }
109 
110 static void
db_read_string(const char * src,size_t len,char * dst)111 db_read_string(const char *src, size_t len, char *dst)
112 {
113           size_t i;
114 
115           for (i = 0; i < len; i++) {
116                     db_read_bytes((db_addr_t)&src[i], 1, &dst[i]);
117                     if (dst[i] == '\0')
118                               break;
119           }
120 }
121 
122 void
db_show_all_procs(db_expr_t addr,bool haddr,db_expr_t count,const char * modif)123 db_show_all_procs(db_expr_t addr, bool haddr, db_expr_t count,
124                       const char *modif)
125 {
126           static struct pgrp pgrp;
127           static proc_t p;
128           static lwp_t l;
129           const char *mode, *ename;
130           proc_t *pp;
131           lwp_t *lp;
132           char db_nbuf[MAXCOMLEN + 1], wbuf[MAXCOMLEN + 1];
133           bool run;
134           int cpuno;
135 
136           if (modif[0] == 0)
137                     mode = "l";                             /* default == lwp mode */
138           else
139                     mode = strchr("mawln", modif[0]);
140 
141           if (mode == NULL || *mode == 'm') {
142                     db_printf("usage: show all procs [/a] [/l] [/n] [/w]\n");
143                     db_printf("\t/a == show process address info\n");
144                     db_printf("\t/l == show LWP info [default]\n");
145                     db_printf("\t/n == show normal process info\n");
146                     db_printf("\t/w == show process wait/emul info\n");
147                     return;
148           }
149 
150           switch (*mode) {
151           case 'a':
152                     db_printf("PID   %-16s %18s %18s %18s\n",
153                         "COMMAND", "STRUCT PROC *", "UAREA *", "VMSPACE/VM_MAP");
154                     break;
155           case 'l':
156                     db_printf("PID   %5s S %3s %9s %18s %18s %-8s\n",
157                         "LID", "CPU", "FLAGS", "STRUCT LWP *", "NAME", "WAIT");
158                     break;
159           case 'n':
160                     db_printf("PID  %8s %8s %10s S %7s %4s %16s %7s\n",
161                         "PPID", "PGRP", "UID", "FLAGS", "LWPS", "COMMAND", "WAIT");
162                     break;
163           case 'w':
164                     db_printf("PID   %5s %16s %8s %4s %-16s %s\n",
165                         "LID", "COMMAND", "EMUL", "PRI", "WAIT-MSG",
166                         "WAIT-CHANNEL");
167                     break;
168           }
169 
170           for (pp = db_proc_first(); pp != NULL; pp = db_proc_next(pp)) {
171                     db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p);
172                     if (p.p_stat == 0) {
173                               continue;
174                     }
175                     lp = p.p_lwps.lh_first;
176                     if (lp != NULL) {
177                               db_read_bytes((db_addr_t)lp, sizeof(l), (char *)&l);
178                     }
179                     db_printf("%-5d", p.p_pid);
180 
181                     switch (*mode) {
182                     case 'a':
183                               db_printf(" %-16.16s %18lx %18lx %18lx\n",
184                                   p.p_comm, (long)pp,
185                                   (long)(lp != NULL ? l.l_addr : 0),
186                                   (long)p.p_vmspace);
187                               break;
188                     case 'l':
189                                while (lp != NULL) {
190                                         if (l.l_name != NULL) {
191                                                   db_read_string(l.l_name,
192                                                       MAXCOMLEN, db_nbuf);
193                                                   db_nbuf[MAXCOMLEN] = '\0';
194                                         } else {
195                                                   strlcpy(db_nbuf, p.p_comm,
196                                                       sizeof(db_nbuf));
197                                         }
198                                         run = (l.l_stat == LSONPROC ||
199                                             (l.l_pflag & LP_RUNNING) != 0);
200                                         if (l.l_cpu != NULL) {
201                                                   db_read_bytes((db_addr_t)
202                                                       &l.l_cpu->ci_data.cpu_index,
203                                                       sizeof(cpuno), (char *)&cpuno);
204                                         } else
205                                                   cpuno = -1;
206                                         if (l.l_wchan && l.l_wmesg) {
207                                                   db_read_string(l.l_wmesg,
208                                                       sizeof(wbuf), wbuf);
209                                                   wbuf[MAXCOMLEN] = '\0';
210                                         } else {
211                                                   wbuf[0] = '\0';
212                                         }
213                                         db_printf("%c%5d %d %3d %9x %18lx %18s %-8s\n",
214                                             (run ? '>' : ' '), l.l_lid,
215                                             l.l_stat, cpuno, l.l_flag, (long)lp,
216                                             db_nbuf, wbuf);
217                                         lp = LIST_NEXT((&l), l_sibling);
218                                         if (lp != NULL) {
219                                                   db_printf("%-5d", p.p_pid);
220                                                   db_read_bytes((db_addr_t)lp, sizeof(l),
221                                                       (char *)&l);
222                                         }
223                               }
224                               break;
225                     case 'n':
226                               db_read_bytes((db_addr_t)p.p_pgrp, sizeof(pgrp),
227                                   (char *)&pgrp);
228                               if (lp != NULL && l.l_wchan && l.l_wmesg) {
229                                         db_read_string(l.l_wmesg,
230                                             sizeof(wbuf), wbuf);
231                                         wbuf[MAXCOMLEN] = '\0';
232                               } else {
233                                         wbuf[0] = '\0';
234                               }
235                               db_printf("%8d %8d %10d %d %#7x %4d %16s %7.7s\n",
236                                   p.p_pptr != NULL ? p.p_pptr->p_pid : -1, pgrp.pg_id,
237 #ifdef _KERNEL
238                                   kauth_cred_getuid(p.p_cred),
239 #else
240                                   /* XXX CRASH(8) */ 666,
241 #endif
242                                   p.p_stat, p.p_flag,
243                                   p.p_nlwps, p.p_comm,
244                                   (p.p_nlwps != 1) ? "*" : wbuf);
245                               break;
246 
247                     case 'w':
248                                while (lp != NULL) {
249                                         if (l.l_wchan && l.l_wmesg) {
250                                                   db_read_string(l.l_wmesg,
251                                                       sizeof(wbuf), wbuf);
252                                                   wbuf[MAXCOMLEN] = '\0';
253                                         } else {
254                                                   wbuf[0] = '\0';
255                                         }
256                                         run = (l.l_stat == LSONPROC ||
257                                             (l.l_pflag & LP_RUNNING) != 0);
258                                         db_read_bytes((db_addr_t)&p.p_emul->e_name,
259                                             sizeof(ename), (char *)&ename);
260 
261                                         db_read_string(ename, sizeof(db_nbuf), db_nbuf);
262                                         db_nbuf[MAXCOMLEN] = '\0';
263 
264                                         db_printf(
265                                             "%c%5d %16s %8s %4d %-16s %-18lx\n",
266                                             (run ? '>' : ' '), l.l_lid,
267                                             p.p_comm, db_nbuf,
268                                             l.l_priority, wbuf, (long)l.l_wchan);
269                                         lp = LIST_NEXT((&l), l_sibling);
270                                         if (lp != NULL) {
271                                                   db_printf("%-5d", p.p_pid);
272                                                   db_read_bytes((db_addr_t)lp, sizeof(l),
273                                                       (char *)&l);
274                                         }
275                               }
276                               break;
277                     }
278           }
279 }
280 
281 void
db_show_proc(db_expr_t addr,bool haddr,db_expr_t count,const char * modif)282 db_show_proc(db_expr_t addr, bool haddr, db_expr_t count, const char *modif)
283 {
284           static proc_t p;
285           static lwp_t l;
286           const char *mode;
287           proc_t *pp;
288           lwp_t *lp;
289           char db_nbuf[MAXCOMLEN + 1], wbuf[MAXCOMLEN + 1];
290           bool run;
291           int cpuno;
292 
293           if (modif[0] == 0)
294                     mode = "p";                             /* default == by pid */
295           else
296                     mode = strchr("ap", modif[0]);
297 
298           if (mode == NULL || !haddr) {
299                     db_printf("usage: show proc [/a] [/p] address|pid\n");
300                     db_printf("\t/a == argument is an address of any lwp\n");
301                     db_printf("\t/p == argument is a pid [default]\n");
302                     return;
303           }
304 
305           switch (*mode) {
306           case 'a':
307                     lp = (lwp_t *)(uintptr_t)addr;
308                     db_printf("lwp_t %lx\n", (long)lp);
309                     db_read_bytes((db_addr_t)lp, sizeof(l), (char *)&l);
310                     pp = l.l_proc;
311                     break;
312           default:
313           case 'p':
314                     pp = db_proc_find((pid_t)addr);
315                     lp = NULL;
316                     break;
317           }
318 
319           if (pp == NULL) {
320                     db_printf("bad address\n");
321                     return;
322           }
323 
324           db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p);
325           if (lp == NULL)
326                     lp = p.p_lwps.lh_first;
327 
328           db_printf("%s: pid %d proc %lx vmspace/map %lx flags %x\n",
329               p.p_comm, p.p_pid, (long)pp, (long)p.p_vmspace, p.p_flag);
330 
331           while (lp != NULL) {
332                     db_read_bytes((db_addr_t)lp, sizeof(l), (char *)&l);
333 
334                     run = (l.l_stat == LSONPROC ||
335                         (l.l_pflag & LP_RUNNING) != 0);
336 
337                     db_printf("%slwp %d", (run ? "> " : "  "), l.l_lid);
338                     if (l.l_name != NULL) {
339                               db_read_string(l.l_name, MAXCOMLEN, db_nbuf);
340                               db_nbuf[MAXCOMLEN] = '\0';
341                               db_printf(" [%s]", db_nbuf);
342                     }
343                     db_printf(" %lx pcb %lx\n", (long)lp, (long)l.l_addr);
344 
345                     if (l.l_cpu != NULL) {
346                               db_read_bytes((db_addr_t)
347                                   &l.l_cpu->ci_data.cpu_index,
348                                   sizeof(cpuno), (char *)&cpuno);
349                     } else
350                               cpuno = -1;
351                     db_printf("    stat %d flags %x cpu %d pri %d ref %d\n",
352                         l.l_stat, l.l_flag, cpuno, l.l_priority, l.l_refcnt);
353 
354                     if (l.l_wchan && l.l_wmesg) {
355                               db_read_string(l.l_wmesg, MAXCOMLEN, wbuf);
356                               wbuf[MAXCOMLEN] = '\0';
357                               db_printf("    wmesg %s wchan %lx\n",
358                                   wbuf, (long)l.l_wchan);
359                     }
360 
361                     lp = LIST_NEXT(&l, l_sibling);
362           }
363 }
364