1 /* hiiosdump.c  -  Hiquu I/O Engine data structure dump
2  * Copyright (c) 2006,2012 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * This is confidential unpublished proprietary source code of the author.
4  * NO WARRANTY, not even implied warranties. Contains trade secrets.
5  * Distribution prohibited unless authorized in writing. See file COPYING.
6  * Special grant: hiios.c may be used with zxid open source project under
7  * same licensing terms as zxid itself.
8  * $Id$
9  *
10  * 15.4.2006, created over Easter holiday --Sampo
11  * 16.8.2012, modified license grant to allow use with ZXID.org --Sampo
12  *
13  * See http://pl.atyp.us/content/tech/servers.html for inspiration on threading strategy.
14  *
15  *   MANY ELEMENTS IN QUEUE            ONE ELEMENT IN Q   EMPTY QUEUE
16  *   consume             produce       consume  produce   consume  produce
17  *    |                   |             | ,-------'         |        |
18  *    v                   v             v v                 v        v
19  *   qel.n --> qel.n --> qel.n --> 0   qel.n --> 0          0        0
20  */
21 
22 #include "platform.h"
23 
24 #include <pthread.h>
25 #include <memory.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 
32 #include "akbox.h"
33 #include "hiproto.h"
34 #include "hiios.h"
35 #include "errmac.h"
36 
37 /* Current color for coloring data structures for integrity checking, e.g.
38  *   gdb p hi_color+=4, hi_sanity_hit(255, hit)
39  * Note that different color MUST be used for each analysis and some
40  * analysis iterations involve several classes of next pointers. This
41  * is solved by having different classes being represented by color plus
42  * offset. However, this means that color must be incremented in steps of 4.
43  *   hi_color+0 qel.n           -- free_pdus, todo_consume
44  *   hi_color+1 pdu->n, io->n   -- reqs
45  *   hi_color+2 pdu->wn         -- to_write
46  * The color takes space as a struct field, thus only 8 or 16 bits are
47  * supplied (varies over time with implementation), which should
48  * allow most debugging chores, but may not be adequate for some. */
49 short hi_color = 4;
50 
51 /*() Sanity check hiios pdu data structures.
52  * Returns number of nodes scanned, or negative for errors. */
53 
54 /* Called by:  hi_sanity_hit, hi_sanity_io x4, hi_sanity_pdu x3, hi_sanity_shf x2 */
55 int hi_sanity_pdu(int mode, struct hi_pdu* root_pdu)
56 {
57   int errs = 0;
58   int nodes = 0;
59   struct hi_pdu* pdu;
60 
61   if (mode&0x80) {
62     if (root_pdu->reals)
63       printf("    pdu_%p  //reals  (%.*s)\n", root_pdu, (int)MIN(root_pdu->ap-root_pdu->m,4), root_pdu->m);
64     else
65       printf("    pdu_%p -> null [label=reals];\n", root_pdu);
66   }
67   for (pdu = root_pdu->reals; pdu; pdu = pdu->n) {
68     if (mode&0x80) printf("    -> pdu_%p  // (%.*s)\n", pdu, (int)MIN(pdu->ap-pdu->m,4), pdu->m);
69     if (pdu->color == hi_color+1) {
70       printf("ERR *** pdu_%p has circular reference (color=%d) wrt pdu->reals pdu->n\n", pdu, pdu->color);
71       --errs;
72       break;
73     } else {
74       pdu->color = hi_color+1;
75       ++nodes;
76       if (mode&0x01) nodes += hi_sanity_pdu(mode, pdu);
77     }
78     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
79       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
80       --errs;
81     }
82   }
83   if (mode&0x80 && root_pdu->reals) printf("    [label=reals];\n");
84 
85   if (mode&0x80) {
86     if (root_pdu->synths)
87       printf("    pdu_%p  // synths\n", root_pdu);
88     else
89       printf("    pdu_%p -> null [label=synths];\n", root_pdu);
90   }
91   for (pdu = root_pdu->synths; pdu; pdu = pdu->n) {
92     if (mode&0x80) printf("    -> pdu_%p\n", pdu);
93     if (pdu->color == hi_color+1) {
94       printf("ERR *** pdu_%p has circular reference (color=%d) wrt pdu->synths pdu->n\n", pdu, pdu->color);
95       --errs;
96       break;
97     } else {
98       pdu->color = hi_color+1;
99       ++nodes;
100       if (mode&0x01) nodes += hi_sanity_pdu(mode, pdu);
101     }
102     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
103       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
104       --errs;
105     }
106   }
107   if (mode&0x80 && root_pdu->synths) printf("    [label=synths];\n");
108 
109   if (mode&0x80) {
110     if (root_pdu->subresps)
111       printf("    pdu_%p  // subresps\n", root_pdu);
112     else
113       printf("    pdu_%p -> null [label=subresps];\n", root_pdu);
114   }
115   for (pdu = root_pdu->subresps; pdu; pdu = pdu->wn) {
116     if (mode&0x80) printf("    -> pdu_%p\n", pdu);
117     if (pdu->color == hi_color+2) {
118       printf("ERR *** pdu_%p has circular reference (color=%d) wrt pdu->subresps pdu->wn\n", pdu, pdu->color);
119       --errs;
120       break;
121     } else {
122       pdu->color = hi_color+2;
123       ++nodes;
124       if (mode&0x01) nodes += hi_sanity_pdu(mode, pdu);
125     }
126     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
127       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
128       --errs;
129     }
130   }
131   if (mode&0x80 && root_pdu->subresps) printf("    [label=subresps];\n");
132 
133   return errs?errs:nodes;
134 }
135 
136 /*() Sanity check hiios io data structures.
137  * Returns number of nodes scanned, or negative for errors. */
138 
139 /* Called by:  hi_sanity_shf */
140 int hi_sanity_io(int mode, struct hi_io* root_io)
141 {
142   int errs = 0;
143   int nodes = 0;
144   struct hi_pdu* pdu;
145 
146   if (root_io->fd & 0x80000000 || root_io->fd == 0) {
147     if (mode&0x80) printf("io_%p [label=nc];  // fd=0x%x\n", root_io, root_io->fd);
148     return 0;
149   }
150 
151   if (mode&0x80) printf("  io_%p -> pdu_%p [label=cur_pdu]; // fd=0x%x\n", root_io, root_io->cur_pdu, root_io->fd);
152   if (root_io->cur_pdu)
153     root_io->cur_pdu->color = hi_color+1;  /* cur_pdu is mutually exclusive with io->reqs */
154 
155   if (mode&0x80) {
156     if (root_io->reqs)
157       printf("  io_%p  // reqs\n", root_io);
158     else
159       printf("  io_%p -> null [label=reqs];\n", root_io);
160   }
161   for (pdu = root_io->reqs; pdu; pdu = pdu->n) {
162     if (mode&0x80) printf("    -> pdu_%p  // (%.*s)\n", pdu, (int)MIN(pdu->ap-pdu->m,4), pdu->m);
163     if (pdu->color == hi_color+1) {
164       printf("ERR *** pdu_%p has circular reference (color=%d) wrt io->reqs pdu->n\n", pdu, pdu->color);
165       --errs;
166       break;
167     } else {
168       pdu->color = hi_color+1;
169       ++nodes;
170       if (mode&0x02) nodes += hi_sanity_pdu(mode, pdu);
171     }
172     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
173       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
174       --errs;
175     }
176   }
177   if (mode&0x80 && root_io->reqs) printf("[label=reqs];\n");
178 
179   if (mode&0x80) {
180     if (root_io->pending)
181       printf("  io_%p  // pending\n", root_io);
182     else
183       printf("  io_%p -> null [label=pending];\n", root_io);
184   }
185   for (pdu = root_io->pending; pdu; pdu = pdu->n) {
186     if (mode&0x80) printf("    -> pdu_%p  // (%.*s)\n", pdu, (int)MIN(pdu->ap-pdu->m,4), pdu->m);
187     if (pdu->color == hi_color+1) {
188       printf("ERR *** pdu_%p has circular reference (color=%d) wrt io->pending pdu->n\n", pdu, pdu->color);
189       --errs;
190       break;
191     } else {
192       pdu->color = hi_color+1;
193       ++nodes;
194       if (mode&0x02) nodes += hi_sanity_pdu(mode, pdu);
195     }
196     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
197       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
198       --errs;
199     }
200   }
201   if (mode&0x80 && root_io->reqs) printf("[label=pending];\n");
202 
203   if (mode&0x80) {
204     if (root_io->to_write_produce) {
205       printf("  io_%p -> pdu_%p [label=to_write_produce]; // (%.*s)\n", root_io, root_io->to_write_produce, (int)MIN(root_io->to_write_produce->ap-root_io->to_write_produce->m,4), root_io->to_write_produce->m);
206       ASSERT(root_io->to_write_produce->wn == 0);
207       /*if (mode&0x02) nodes += hi_sanity_pdu(mode, root_io->to_write_produce);*/
208       if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
209 	printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
210 	--errs;
211       }
212     } else
213       printf("  io_%p -> null [label=to_write_produce];\n", root_io);
214   }
215 
216   if (mode&0x80) {
217     if (root_io->to_write_consume)
218       printf("  io_%p  // to_write_consume\n", root_io);
219     else
220       printf("  io_%p -> null [label=to_write_consume];  // n_to_write=%d\n", root_io, root_io->n_to_write);
221   }
222   for (pdu = root_io->to_write_consume; pdu; pdu = pdu->wn) {
223     if (mode&0x80) printf("    -> pdu_%p  // (%.*s)\n", pdu, (int)MIN(pdu->ap-pdu->m,4), pdu->m);
224     if (pdu->color == hi_color+2) {
225       printf("ERR *** pdu_%p has circular reference (color=%d) wrt io->to_write_consume pdu->wn\n", pdu, pdu->color);
226       --errs;
227       break;
228     } else {
229       pdu->color = hi_color+2;
230       ++nodes;
231       if (mode&0x02) nodes += hi_sanity_pdu(mode, pdu);
232     }
233     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
234       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
235       --errs;
236     }
237   }
238   if (mode&0x80 && root_io->to_write_consume) printf("[label=to_write_consume];  // n_to_write=%d\n", root_io->n_to_write);
239 
240   if (mode&0x80) {
241     if (root_io->in_write)
242       printf("io_%p  // in_write\n", root_io);
243     else
244       printf("  io_%p -> null [label=in_write];\n", root_io);
245   }
246   for (pdu = root_io->in_write; pdu; pdu = pdu->wn) {
247     if (mode&0x80) printf("-> pdu_%p  // (%.*s)\n", pdu, (int)MIN(pdu->ap-pdu->m,4), pdu->m);
248     if (pdu->color == hi_color+2) {
249       printf("ERR *** pdu_%p has circular reference (color=%d) wrt io->in_write pdu->wn\n", pdu, pdu->color);
250       --errs;
251       break;
252     } else {
253       pdu->color = hi_color+2;
254       ++nodes;
255       if (mode&0x02) nodes += hi_sanity_pdu(mode, pdu);
256     }
257     if (pdu->qel.intodo != HI_INTODO_PDUINUSE) {
258       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_PDUINUSE);
259       --errs;
260     }
261   }
262   if (mode&0x80 && root_io->in_write) printf("[label=in_write];\n");
263 
264   return errs?errs:nodes;
265 }
266 
267 /*() Sanity check hiios thread data structures.
268  * Returns number of nodes scanned, or negative for errors. */
269 
270 /* Called by:  hi_dump, hi_sanity */
271 int hi_sanity_hit(int mode, struct hi_thr* root_hit)
272 {
273   int errs = 0;
274   int nodes = 0;
275   struct hi_pdu* pdu;
276 
277   printf("hit_%p [label=\"tid_%x\"];\n", root_hit, (unsigned int)root_hit->self);
278   if (mode&0x80) {
279     if (root_hit->free_pdus)
280       printf("hit_%p  // free_pdus\n", root_hit);
281     else
282       printf("hit_%p -> null [label=free_pdus];\n", root_hit);
283   }
284   for (pdu = root_hit->free_pdus; pdu; pdu = (struct hi_pdu*)pdu->qel.n) {
285     if (mode&0x80) printf("-> pdu_%p  // (%.*s)\n", pdu, (int)MIN(pdu->ap-pdu->m,4), pdu->m);
286     if (pdu->color == hi_color+1) {
287       printf("ERR *** pdu_%p in hit->free_pdus is also in reqs list (color=%d)\n", pdu, pdu->color);
288       --errs;
289       break;
290     }
291     if (pdu->color == hi_color+0) {
292       printf("ERR *** pdu_%p has circular reference (color=%d) wrt hit->free_pdus pdu->qel.n\n", pdu, pdu->color);
293       --errs;
294       break;
295     }
296     if (pdu->qel.intodo != HI_INTODO_HIT_FREE) {
297       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_HIT_FREE);
298       --errs;
299     }
300     pdu->color = hi_color+0;
301     ++nodes;
302     if (!(mode&0x08)) nodes += hi_sanity_pdu(mode, pdu);
303   }
304   if (mode&0x80 && root_hit->free_pdus) printf("[label=free_pdus];\n");
305 
306   return errs?errs:nodes;
307 }
308 
309 /*() Sanity check hiios shuffler data structures, i.e. practically everything.
310  * Mainly meant to be called from gdb like this: p hi_color+=4, hi_sanity(255, shf)
311  * The mode argument controls recursion and output of a visualization
312  * of the data structure. It consists of bits as follows
313  *  76543210
314  *  |   |||`-- recurse on sub PDU         (0x01)
315  *  |   ||`--- recurse on PDU             (0x02)
316  *  |   |`---- recurse on IO              (0x04)
317  *  |   `----- do not recurse on free_pdu (0x08)
318  *  `--------- print                      (0x80)
319  * Simple way to enable all recusion is to use mode=127 or mode=255 if print is desired
320  * Returns negative number (of errors) if errors are found. Otherwise positive
321  * number representing the rough number of nodes traversed (size of the data structure)
322  * is returned.
323  * hi_sanity() will most probably crash upon corrupt pointers. It will make
324  * an attempt to detect illegal circular data structures (see hi_color).
325  *   (gdb) p hi_color+=4, hi_sanity(255, shuff)
326  */
327 
328 /* Called by:  hi_dump, hi_sanity, hi_shuffle, zxbusd_main */
329 int hi_sanity_shf(int mode, struct hiios* root_shf)
330 {
331   int res;
332   int errs = 0;
333   int nodes = 0;
334   struct hi_qel* qe;
335   struct hi_pdu* pdu;
336   struct hi_io* io;
337 
338   if (mode&0x80) {
339     if (root_shf->ios)
340       printf("shf_%p  // max_ios=%d\n", root_shf, root_shf->max_ios);
341     else
342       printf("shf_%p -> null [label=ios];\n", root_shf);
343   }
344   for (io = root_shf->ios; io < root_shf->ios + root_shf->max_ios; ++io) {
345     if (!io->n_thr && (io->fd & 0x80000000 || io->fd == 0)) {
346       /*ASSERT(io->qel.intodo == HI_INTODO_SHF_FREE);  doesn't hold betw 1st close and end game */
347       printf("io_%p  // free? fd(%x) n_c/t=%d/%d in_todo=%d\n", io, io->fd, io->n_close, io->n_thr, io->qel.intodo);
348       continue;   /* ios slot not in use */
349     }
350     if (mode&0x80) printf("-> io_%p\n", io);
351     ++nodes;
352     if (mode&0x04) {
353       res = hi_sanity_io(mode, io);
354       if (res < 0)
355 	errs += res;
356       else
357 	nodes += res;
358     }
359   }
360   if (mode&0x80 && root_shf->ios) printf("[label=ios];\n");
361 
362   if (mode&0x80) {
363     if (root_shf->todo_consume)
364       printf("shf_%p   // todo_consume (color=%d)\n", root_shf, hi_color+0);
365     else
366       printf("shf_%p -> null [label=todo_consume];\n", root_shf);
367   }
368   for (qe = root_shf->todo_consume; qe; qe = qe->n) {
369     if (mode&0x80) printf("-> qe_%p\n", qe);
370 #if 0
371     if (pdu->color == hi_color+0) {
372       printf("ERR *** pdu_%p has circular reference (color=%d) wrt hit->free_pdus pdu->qel.n\n", pdu, pdu->color);
373       --errs;
374       break;
375     }
376     pdu->color = hi_color+0;
377     ++nodes;
378     if (!(mode&0x08)) {
379       res = hi_sanity_pdu(mode, pdu);
380       if (res < 0)
381 	errs += res;
382       else
383 	nodes += res;
384     }
385 #endif
386     if (qe->intodo != HI_INTODO_INTODO) {
387       printf("ERR *** qe_%p has wrong intodo=%x expected %x\n", qe, qe->intodo, HI_INTODO_INTODO);
388       --errs;
389     }
390   }
391   if (mode&0x80 && root_shf->todo_consume) printf("[label=todo_consume];\n");
392 
393   if (mode&0x80) {
394     if (root_shf->free_pdus)
395       printf("shf_%p   // free_pdus (color=%d)\n", root_shf, hi_color+0);
396     else
397       printf("shf_%p -> null [label=free_pdus];\n", root_shf);
398   }
399   for (pdu = root_shf->free_pdus; pdu; pdu = (struct hi_pdu*)pdu->qel.n) {
400     if (mode&0x80) printf("-> pdu_%p ", pdu);
401     if (pdu->color == hi_color+1) {
402       printf("ERR *** pdu_%p in free list is also in reqs list (color=%d)\n", pdu, pdu->color);
403       --errs;
404       break;
405     }
406     if (pdu->color == hi_color+0) {
407       printf("ERR *** pdu_%p has circular reference (color=%d) wrt hit->free_pdus pdu->qel.n\n", pdu, pdu->color);
408       --errs;
409       break;
410     }
411     if (pdu->qel.intodo != HI_INTODO_SHF_FREE) {
412       printf("ERR *** pdu_%p has wrong intodo=%x expected %x\n", pdu, pdu->qel.intodo, HI_INTODO_SHF_FREE);
413       --errs;
414     }
415     pdu->color = hi_color+0;
416     ++nodes;
417     if (!(mode&0x08)) {
418       res = hi_sanity_pdu(mode, pdu);
419       if (res < 0)
420 	errs += res;
421       else
422 	nodes += res;
423     }
424   }
425   if (mode&0x80 && root_shf->free_pdus) printf("[label=free_pdus];\n");
426 
427   return errs?errs:nodes;
428 }
429 
430 /*() hi_sanity is called by macro HI_SANITY() and is meant to be called from gdb interactively.
431  * Returns number of nodes scanned, or negative for errors. */
432 
433 /* Called by: */
434 int hi_sanity(int mode, struct hiios* root_shf, struct hi_thr* root_hit, const char* fn, int line)
435 {
436   int res;
437   hi_color += 4;
438   if (root_shf) {
439     res = hi_sanity_shf(mode, root_shf);
440     D("Data structure dump %d\n---------------------- %s:%d", res, fn, line);
441     ASSERT(res >= 0);
442   }
443   if (root_hit) {
444     res = hi_sanity_hit(mode, root_hit);
445     D("Hit structure dump %d\n====================== %s:%d", res, fn, line);
446     ASSERT(res >= 0);
447   }
448   return 0;
449 }
450 
451 /*() All thread data structure check.
452  * Returns number of nodes scanned, or negative for errors. */
453 
454 /* Called by: */
455 int hi_dump(struct hiios* shf)
456 {
457   struct hi_thr* hit;
458   int res = hi_sanity_shf(255, shf);
459   hi_color += 4;
460   D("Dumping shf=%p hi_color=%d", shf, hi_color);
461   printf("Data structure dump %d\n----------------------\n", res);
462   for (hit = shf->threads; hit; hit = hit->n) {
463     res = hi_sanity_hit(255, hit);
464     printf("Hit structure dump %d\n======================\n", res);
465   }
466   return res;
467 }
468 
469 /* EOF  --  hiiosdump.c */
470