xref: /dragonfly/test/debug/vmpqinactive.c (revision b9a6fe0869cc7b508bd0c79e920be09e2a206d4e)
1 /*
2  * VMPQINACTIVE.C
3  *
4  * cc -I/usr/src/sys vmpqinactive.c -o ~/bin/vmpqinactive -lkvm
5  *
6  * vmpqinactive
7  *
8  * Calculate how many inactive pages are dirty
9  *
10  * Copyright (c) 2004-2020 The DragonFly Project.  All rights reserved.
11  *
12  * This code is derived from software contributed to The DragonFly Project
13  * by Matthew Dillon <dillon@backplane.com>
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  *
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in
23  *    the documentation and/or other materials provided with the
24  *    distribution.
25  * 3. Neither the name of The DragonFly Project nor the names of its
26  *    contributors may be used to endorse or promote products derived
27  *    from this software without specific, prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
33  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
35  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
37  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
38  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
39  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  */
42 
43 #define _KERNEL_STRUCTURES_
44 #include <sys/param.h>
45 #include <sys/user.h>
46 #include <sys/malloc.h>
47 #include <sys/signalvar.h>
48 #include <sys/vnode.h>
49 #include <sys/buf.h>
50 #include <sys/namecache.h>
51 #include <sys/slaballoc.h>
52 
53 #include <vm/vm.h>
54 #include <vm/vm_page.h>
55 #include <vm/vm_kern.h>
56 #include <vm/vm_object.h>
57 #include <vm/swap_pager.h>
58 #include <vm/vnode_pager.h>
59 
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <fcntl.h>
64 #include <kvm.h>
65 #include <nlist.h>
66 #include <getopt.h>
67 
68 struct nlist Nl[] = {
69     { "_vm_page_array" },
70     { "_vm_page_array_size" },
71     { "_kernel_object" },
72     { "_nbuf" },
73     { "_nswbuf_mem" },
74     { "_nswbuf_kva" },
75     { "_nswbuf_raw" },
76     { "_kernbase" },
77     { "__end" },
78     { NULL }
79 };
80 
81 int debugopt;
82 int verboseopt;
83 #if 0
84 struct vm_page **vm_page_buckets;
85 int vm_page_hash_mask;
86 #endif
87 struct vm_page *vm_page_array;
88 struct vm_object *kernel_object_ptr;
89 int vm_page_array_size;
90 long nbuf;
91 long nswbuf_mem;
92 long nswbuf_kva;
93 long nswbuf_raw;
94 long kern_size;
95 
96 void checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj);
97 static void kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m);
98 static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
99 static int kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
100 
101 #if 0
102 static void addsltrack(vm_page_t m);
103 static void dumpsltrack(kvm_t *kd);
104 #endif
105 static int unique_object(void *ptr);
106 
107 long count_free;
108 long count_wired;             /* total */
109 long count_wired_vnode;
110 long count_wired_anon;
111 long count_wired_in_pmap;
112 long count_wired_pgtable;
113 long count_wired_other;
114 long count_wired_kernel;
115 long count_wired_obj_other;
116 
117 long count_anon;
118 long count_anon_in_pmap;
119 long count_vnode;
120 long count_device;
121 long count_phys;
122 long count_kernel;
123 long count_unknown;
124 long count_noobj_offqueue;
125 long count_noobj_onqueue;
126 
127 int
main(int ac,char ** av)128 main(int ac, char **av)
129 {
130     const char *corefile = NULL;
131     const char *sysfile = NULL;
132     struct vm_page m;
133     struct vm_object obj;
134     kvm_t *kd;
135     int ch;
136 #if 0
137     vm_page_t mptr;
138     int hv;
139 #endif
140     int i;
141     const char *qstr;
142     const char *ostr;
143     long pqinactive_clean;
144     long pqinactive_dirty1;
145     long pqinactive_dirty2;
146     long pqinactive_refd;
147     long pqinactive_ready;
148 
149     pqinactive_clean = 0;
150     pqinactive_dirty1 = 0;
151     pqinactive_dirty2 = 0;
152     pqinactive_refd = 0;
153     pqinactive_ready = 0;
154 
155     while ((ch = getopt(ac, av, "M:N:dv")) != -1) {
156           switch(ch) {
157           case 'd':
158               ++debugopt;
159               break;
160           case 'v':
161               ++verboseopt;
162               break;
163           case 'M':
164               corefile = optarg;
165               break;
166           case 'N':
167               sysfile = optarg;
168               break;
169           default:
170               fprintf(stderr, "%s [-M core] [-N system]\n", av[0]);
171               exit(1);
172           }
173     }
174     ac -= optind;
175     av += optind;
176 
177     if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) {
178           perror("kvm_open");
179           exit(1);
180     }
181     if (kvm_nlist(kd, Nl) != 0) {
182           perror("kvm_nlist");
183           exit(1);
184     }
185 
186     kkread(kd, Nl[0].n_value, &vm_page_array, sizeof(vm_page_array));
187     kkread(kd, Nl[1].n_value, &vm_page_array_size, sizeof(vm_page_array_size));
188     kernel_object_ptr = (void *)Nl[2].n_value;
189     kkread(kd, Nl[3].n_value, &nbuf, sizeof(nbuf));
190     kkread(kd, Nl[4].n_value, &nswbuf_mem, sizeof(nswbuf_mem));
191     kkread(kd, Nl[5].n_value, &nswbuf_kva, sizeof(nswbuf_kva));
192     kkread(kd, Nl[6].n_value, &nswbuf_raw, sizeof(nswbuf_raw));
193     kern_size = Nl[8].n_value - Nl[7].n_value;
194 
195     /*
196      * Scan the vm_page_array validating all pages with associated objects
197      */
198     for (i = 0; i < vm_page_array_size; ++i) {
199           if (debugopt && (i & 1023) == 0) {
200               printf("page %d/%d\r", i, vm_page_array_size);
201               fflush(stdout);
202           }
203           kkread_vmpage(kd, (u_long)&vm_page_array[i], &m);
204           if (m.object) {
205               kkread(kd, (u_long)m.object, &obj, sizeof(obj));
206               checkpage(kd, &vm_page_array[i], &m, &obj);
207           }
208           if (m.queue >= PQ_HOLD) {
209               qstr = "HOLD";
210           } else if (m.queue >= PQ_CACHE) {
211               qstr = "CACHE";
212           } else if (m.queue >= PQ_ACTIVE) {
213               qstr = "ACTIVE";
214           } else if (m.queue >= PQ_INACTIVE) {
215               qstr = "INACTIVE";
216               if (m.dirty || m.wire_count || m.busy_count || m.hold_count ||
217                     (m.flags & PG_NEED_COMMIT)) {
218                         if (m.flags & PG_WINATCFLS)
219                                   ++pqinactive_dirty2;
220                         else
221                                   ++pqinactive_dirty1;
222               } else {
223                         ++pqinactive_clean;
224                         if (m.flags & PG_REFERENCED)
225                                   ++pqinactive_refd;
226                         else
227                                   ++pqinactive_ready;
228               }
229           } else if (m.queue >= PQ_FREE) {
230               qstr = "FREE";
231               ++count_free;
232           } else {
233               qstr = "NONE";
234           }
235           if (m.wire_count) {
236                     ++count_wired;
237                     if (m.object == NULL) {
238                               if ((m.flags & PG_MAPPED) &&
239                                   (m.flags & PG_WRITEABLE) &&
240                                   (m.flags & PG_UNQUEUED)) {
241                                         ++count_wired_pgtable;
242                               } else {
243                                         ++count_wired_other;
244                               }
245                     } else if (m.object == kernel_object_ptr) {
246                               ++count_wired_kernel;
247                     } else {
248                               switch(obj.type) {
249                               case OBJT_VNODE:
250                                         ++count_wired_vnode;
251                                         break;
252                               case OBJT_DEFAULT:
253                               case OBJT_SWAP:
254                                         if (m.flags & PG_MAPPED)
255                                                   ++count_wired_in_pmap;
256                                         else
257                                                   ++count_wired_anon;
258                                         break;
259                               default:
260                                         ++count_wired_obj_other;
261                                         break;
262                               }
263                     }
264           } else
265           if (m.flags & PG_MAPPED) {
266                     if (m.object && m.object != kernel_object_ptr) {
267                               switch(obj.type) {
268                               case OBJT_DEFAULT:
269                               case OBJT_SWAP:
270                                         ++count_anon_in_pmap;
271                                         break;
272                               default:
273                                         break;
274                               }
275                     }
276           }
277 
278           if (verboseopt) {
279               printf("page %p obj %p/%-8ju(%016jx) val=%02x dty=%02x hold=%d "
280                        "wire=%-2d act=%-3d busy=%d w/pmapcnt=%d/%d %8s",
281                     &vm_page_array[i],
282                     m.object,
283                     (intmax_t)m.pindex,
284                     (intmax_t)m.pindex * PAGE_SIZE,
285                     m.valid,
286                     m.dirty,
287                     m.hold_count,
288                     m.wire_count,
289                     m.act_count,
290                     m.busy_count,
291                     ((m.flags & PG_WRITEABLE) != 0),
292                     ((m.flags & PG_MAPPED) != 0),
293                     qstr
294               );
295           }
296 
297           if (m.object == kernel_object_ptr) {
298                     ostr = "kernel";
299                     if (unique_object(m.object))
300                               count_kernel += obj.resident_page_count;
301           } else if (m.object) {
302               switch(obj.type) {
303               case OBJT_DEFAULT:
304                     ostr = "default";
305                     if (unique_object(m.object))
306                               count_anon += obj.resident_page_count;
307                     break;
308               case OBJT_SWAP:
309                     ostr = "swap";
310                     if (unique_object(m.object))
311                               count_anon += obj.resident_page_count;
312                     break;
313               case OBJT_VNODE:
314                     ostr = "vnode";
315                     if (unique_object(m.object))
316                               count_vnode += obj.resident_page_count;
317                     break;
318               case OBJT_DEVICE:
319                     ostr = "device";
320                     if (unique_object(m.object))
321                               count_device += obj.resident_page_count;
322                     break;
323               case OBJT_PHYS:
324                     ostr = "phys";
325                     if (unique_object(m.object))
326                               count_phys += obj.resident_page_count;
327                     break;
328               case OBJT_DEAD:
329                     ostr = "dead";
330                     if (unique_object(m.object))
331                               count_unknown += obj.resident_page_count;
332                     break;
333               default:
334                     if (unique_object(m.object))
335                               count_unknown += obj.resident_page_count;
336                     ostr = "unknown";
337                     break;
338               }
339           } else {
340               ostr = "-";
341               if (m.queue == PQ_NONE)
342                         ++count_noobj_offqueue;
343               else if (m.queue - m.pc != PQ_FREE)
344                         ++count_noobj_onqueue;
345           }
346 
347           if (verboseopt) {
348               printf(" %-7s", ostr);
349               if (m.busy_count & PBUSY_LOCKED)
350                     printf(" BUSY");
351               if (m.busy_count & PBUSY_WANTED)
352                     printf(" WANTED");
353               if (m.flags & PG_WINATCFLS)
354                     printf(" WINATCFLS");
355               if (m.flags & PG_FICTITIOUS)
356                     printf(" FICTITIOUS");
357               if (m.flags & PG_WRITEABLE)
358                     printf(" WRITEABLE");
359               if (m.flags & PG_MAPPED)
360                     printf(" MAPPED");
361               if (m.flags & PG_NEED_COMMIT)
362                     printf(" NEED_COMMIT");
363               if (m.flags & PG_REFERENCED)
364                     printf(" REFERENCED");
365               if (m.flags & PG_CLEANCHK)
366                     printf(" CLEANCHK");
367               if (m.busy_count & PBUSY_SWAPINPROG)
368                     printf(" SWAPINPROG");
369               if (m.flags & PG_NOSYNC)
370                     printf(" NOSYNC");
371               if (m.flags & PG_UNQUEUED)
372                     printf(" UNQUEUED");
373               if (m.flags & PG_MARKER)
374                     printf(" MARKER");
375               if (m.flags & PG_RAM)
376                     printf(" RAM");
377               if (m.flags & PG_SWAPPED)
378                     printf(" SWAPPED");
379 #if 0
380               if (m.flags & PG_SLAB)
381                     printf(" SLAB");
382 #endif
383               printf("\n");
384 #if 0
385               if (m.flags & PG_SLAB)
386                     addsltrack(&m);
387 #endif
388           }
389     }
390     if (debugopt || verboseopt)
391           printf("\n");
392     printf("%8.2fM free\n",
393           count_free * 4096.0 / 1048576.0);
394     printf("%8.2fM inactive-clean\n",
395           pqinactive_clean * 4096.0 / 1048576.0);
396     printf("%8.2fM inactive-clean-and-referenced\n",
397           pqinactive_refd * 4096.0 / 1048576.0);
398     printf("%8.2fM inactive-clean-and-ready\n",
399           pqinactive_ready * 4096.0 / 1048576.0);
400     printf("%8.2fM inactive-dirty/first-LRU\n",
401           pqinactive_dirty1 * 4096.0 / 1048576.0);
402     printf("%8.2fM inactive-dirty/second-LRU\n",
403           pqinactive_dirty2 * 4096.0 / 1048576.0);
404 
405     printf("%8.2fM wired vnode (in buffer cache)\n",
406           count_wired_vnode * 4096.0 / 1048576.0);
407     printf("%8.2fM wired in-pmap (probably vnode pages also in buffer cache)\n",
408           count_wired_in_pmap * 4096.0 / 1048576.0);
409     printf("%8.2fM wired pgtable\n",
410           count_wired_pgtable * 4096.0 / 1048576.0);
411     printf("%8.2fM wired anon\n",
412           count_wired_anon * 4096.0 / 1048576.0);
413     printf("%8.2fM wired kernel_object\n",
414           count_wired_kernel * 4096.0 / 1048576.0);
415 
416           printf("\t%8.2fM vm_page_array\n",
417               vm_page_array_size * sizeof(struct vm_page) / 1048576.0);
418           printf("\t%8.2fM buf, swbuf_mem, swbuf_kva, swbuf_raw\n",
419               (nbuf + nswbuf_mem + nswbuf_kva + nswbuf_raw) *
420               sizeof(struct buf) / 1048576.0);
421           printf("\t%8.2fM kernel binary\n", kern_size / 1048576.0);
422           printf("\t(also add in KMALLOC id kmapinfo, or loosely, vmstat -m)\n");
423 
424     printf("%8.2fM wired other (unknown object)\n",
425           count_wired_obj_other * 4096.0 / 1048576.0);
426     printf("%8.2fM wired other (no object, probably kernel)\n",
427           count_wired_other * 4096.0 / 1048576.0);
428 
429     printf("%8.2fM WIRED TOTAL\n",
430           count_wired * 4096.0 / 1048576.0);
431 
432     printf("\n");
433     printf("%8.2fM anonymous (total, includes in-pmap)\n",
434           count_anon * 4096.0 / 1048576.0);
435     printf("%8.2fM anonymous memory in-pmap\n",
436           count_anon_in_pmap * 4096.0 / 1048576.0);
437     printf("%8.2fM vnode (includes wired)\n",
438           count_vnode * 4096.0 / 1048576.0);
439     printf("%8.2fM device\n", count_device * 4096.0 / 1048576.0);
440     printf("%8.2fM phys\n", count_phys * 4096.0 / 1048576.0);
441     printf("%8.2fM kernel (includes wired)\n",
442           count_kernel * 4096.0 / 1048576.0);
443     printf("%8.2fM unknown\n", count_unknown * 4096.0 / 1048576.0);
444     printf("%8.2fM no_object, off queue (includes wired w/o object)\n",
445           count_noobj_offqueue * 4096.0 / 1048576.0);
446     printf("%8.2fM no_object, on non-free queue (includes wired w/o object)\n",
447           count_noobj_onqueue * 4096.0 / 1048576.0);
448 
449 #if 0
450     /*
451      * Scan the vm_page_buckets array validating all pages found
452      */
453     for (i = 0; i <= vm_page_hash_mask; ++i) {
454           if (debugopt) {
455               printf("index %d\r", i);
456               fflush(stdout);
457           }
458           kkread(kd, (u_long)&vm_page_buckets[i], &mptr, sizeof(mptr));
459           while (mptr) {
460               kkread(kd, (u_long)mptr, &m, sizeof(m));
461               if (m.object) {
462                     kkread(kd, (u_long)m.object, &obj, sizeof(obj));
463                     hv = ((uintptr_t)m.object + m.pindex) ^ obj.hash_rand;
464                     hv &= vm_page_hash_mask;
465                     if (i != hv)
466                         printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
467                               " should be in bucket %d\n", i, mptr, hv);
468                     checkpage(kd, mptr, &m, &obj);
469               } else {
470                     printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
471                               " has no object\n", i, mptr);
472               }
473               mptr = m.hnext;
474           }
475     }
476 #endif
477     if (debugopt)
478           printf("\n");
479 #if 0
480     dumpsltrack(kd);
481 #endif
482     return(0);
483 }
484 
485 /*
486  * A page with an object.
487  */
488 void
checkpage(kvm_t * kd,vm_page_t mptr,vm_page_t m,struct vm_object * obj)489 checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj)
490 {
491 #if 0
492     struct vm_page scan;
493     vm_page_t scanptr;
494     int hv;
495 
496     hv = ((uintptr_t)m->object + m->pindex) ^ obj->hash_rand;
497     hv &= vm_page_hash_mask;
498     kkread(kd, (u_long)&vm_page_buckets[hv], &scanptr, sizeof(scanptr));
499     while (scanptr) {
500           if (scanptr == mptr)
501               break;
502           kkread(kd, (u_long)scanptr, &scan, sizeof(scan));
503           scanptr = scan.hnext;
504     }
505     if (scanptr) {
506           if (debugopt > 1)
507               printf("good checkpage %p bucket %d\n", mptr, hv);
508     } else {
509           printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
510                     " page not found in bucket list\n", hv, mptr);
511     }
512 #endif
513 }
514 
515 /*
516  * Acclerate the reading of VM pages
517  */
518 #define VPCACHE_SIZE          65536
519 
520 static void
kkread_vmpage(kvm_t * kd,u_long addr,vm_page_t m)521 kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m)
522 {
523     static struct vm_page vpcache[VPCACHE_SIZE];
524     static u_long vpbeg;
525     static u_long vpend;
526 
527     if (addr < vpbeg || addr >= vpend) {
528           vpbeg = addr;
529           vpend = addr + VPCACHE_SIZE * sizeof(*m);
530           if (vpend > (u_long)(uintptr_t)vm_page_array +
531                         vm_page_array_size * sizeof(*m)) {
532               vpend = (u_long)(uintptr_t)vm_page_array +
533                         vm_page_array_size * sizeof(*m);
534           }
535           kkread(kd, vpbeg, vpcache, vpend - vpbeg);
536     }
537     *m = vpcache[(addr - vpbeg) / sizeof(*m)];
538 }
539 
540 static void
kkread(kvm_t * kd,u_long addr,void * buf,size_t nbytes)541 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
542 {
543     if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
544         perror("kvm_read");
545         exit(1);
546     }
547 }
548 
549 static int
kkread_err(kvm_t * kd,u_long addr,void * buf,size_t nbytes)550 kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
551 {
552     if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
553           return 1;
554     }
555     return 0;
556 }
557 
558 struct SLTrack {
559         struct SLTrack *next;
560         u_long addr;
561 };
562 
563 #define SLHSIZE 1024
564 #define SLHMASK (SLHSIZE - 1)
565 
566 struct SLTrack *SLHash[SLHSIZE];
567 
568 #if 0
569 static
570 void
571 addsltrack(vm_page_t m)
572 {
573           struct SLTrack *slt;
574           u_long addr = (m->pindex * PAGE_SIZE) & ~131071L;
575           int i;
576 
577           if (m->wire_count == 0 || (m->flags & PG_MAPPED) == 0 ||
578               m->object == NULL)
579                     return;
580 
581           i = (addr / 131072) & SLHMASK;
582           for (slt = SLHash[i]; slt; slt = slt->next) {
583                     if (slt->addr == addr)
584                               break;
585           }
586           if (slt == NULL) {
587                     slt = malloc(sizeof(*slt));
588                     slt->addr = addr;
589                     slt->next = SLHash[i];
590                     SLHash[i] = slt;
591           }
592 }
593 #endif
594 
595 static
596 void
dumpsltrack(kvm_t * kd)597 dumpsltrack(kvm_t *kd)
598 {
599           struct SLTrack *slt;
600           int i;
601           long total_zones = 0;
602           long full_zones = 0;
603 
604           for (i = 0; i < SLHSIZE; ++i) {
605                     for (slt = SLHash[i]; slt; slt = slt->next) {
606                               SLZone z;
607 
608                               if (kkread_err(kd, slt->addr, &z, sizeof(z))) {
609                                         printf("SLZone 0x%016lx not mapped\n",
610                                                   slt->addr);
611                                         continue;
612                               }
613                               printf("SLZone 0x%016lx { mag=%08x cpu=%-2d NFree=%-3d "
614                                      "chunksz=%-5d }\n",
615                                      slt->addr,
616                                      z.z_Magic,
617                                      z.z_Cpu,
618                                      z.z_NFree,
619                                      z.z_ChunkSize
620                               );
621                               ++total_zones;
622                               if (z.z_NFree == 0)
623                                         ++full_zones;
624                     }
625           }
626           printf("FullZones/TotalZones: %ld/%ld\n", full_zones, total_zones);
627 }
628 
629 #define HASH_SIZE   (1024*1024)
630 #define HASH_MASK   (HASH_SIZE - 1)
631 
632 struct dup_entry {
633           struct dup_entry *next;
634           void      *ptr;
635 };
636 
637 struct dup_entry *dup_hash[HASH_SIZE];
638 
639 static int
unique_object(void * ptr)640 unique_object(void *ptr)
641 {
642           struct dup_entry *hen;
643           int hv;
644 
645           hv = (intptr_t)ptr ^ ((intptr_t)ptr >> 20);
646           hv &= HASH_MASK;
647           for (hen = dup_hash[hv]; hen; hen = hen->next) {
648                     if (hen->ptr == ptr)
649                               return 0;
650           }
651           hen = malloc(sizeof(*hen));
652           hen->next = dup_hash[hv];
653           hen->ptr = ptr;
654           dup_hash[hv] = hen;
655 
656           return 1;
657 }
658