1 /* Portable data dumper for SXEmacs.
2 Copyright (C) 1999-2000 Olivier Galibert
3 Copyright (C) 2001 Martin Buchholz
5 This file is part of SXEmacs
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 /* Synched up with: Not in FSF. */
26 #include "specifier.h"
29 #include "ui/console-stream.h"
47 Dynarr_declare(pdump_opaque);
48 } pdump_opaque_dynarr;
52 const struct struct_description *desc;
53 } pdump_root_struct_ptr;
56 Dynarr_declare(pdump_root_struct_ptr);
57 } pdump_root_struct_ptr_dynarr;
62 } pdump_static_Lisp_Object;
65 char **address; /* char * for ease of doing relocation */
67 } pdump_static_pointer;
69 static pdump_opaque_dynarr *pdump_opaques;
70 static pdump_root_struct_ptr_dynarr *pdump_root_struct_ptrs;
71 static Lisp_Object_ptr_dynarr *pdump_root_objects;
72 static Lisp_Object_ptr_dynarr *pdump_weak_object_chains;
74 /* Mark SIZE bytes at non-heap address VARADDRESS for dumping as is,
75 without any bit-twiddling. */
76 void dump_add_opaque(void *varaddress, size_t size)
79 info.varaddress = varaddress;
81 if (pdump_opaques == NULL)
82 pdump_opaques = Dynarr_new(pdump_opaque);
83 Dynarr_add(pdump_opaques, info);
86 /* Mark the struct described by DESC and pointed to by the pointer at
87 non-heap address VARADDRESS for dumping.
88 All the objects reachable from this pointer will also be dumped. */
90 dump_add_root_struct_ptr(void *ptraddress,
91 const struct struct_description *desc)
93 pdump_root_struct_ptr info;
94 info.ptraddress = (void **)ptraddress;
96 if (pdump_root_struct_ptrs == NULL)
97 pdump_root_struct_ptrs = Dynarr_new(pdump_root_struct_ptr);
98 Dynarr_add(pdump_root_struct_ptrs, info);
101 /* Mark the Lisp_Object at non-heap address VARADDRESS for dumping.
102 All the objects reachable from this var will also be dumped. */
103 void dump_add_root_object(Lisp_Object * varaddress)
105 if (pdump_root_objects == NULL)
107 Dynarr_new2(Lisp_Object_ptr_dynarr, Lisp_Object *);
108 Dynarr_add(pdump_root_objects, varaddress);
111 /* Mark the list pointed to by the Lisp_Object at VARADDRESS for dumping. */
112 void dump_add_weak_object_chain(Lisp_Object * varaddress)
114 if (pdump_weak_object_chains == NULL)
115 pdump_weak_object_chains =
116 Dynarr_new2(Lisp_Object_ptr_dynarr, Lisp_Object *);
117 Dynarr_add(pdump_weak_object_chains, varaddress);
120 inline static void pdump_align_stream(FILE * stream, size_t alignment)
122 long offset = ftell(stream);
123 long adjustment = ALIGN_SIZE(offset, alignment) - offset;
125 fseek(stream, adjustment, SEEK_CUR);
128 #define PDUMP_ALIGN_OUTPUT(type) pdump_align_stream (pdump_out, ALIGNOF (type))
130 #define PDUMP_WRITE(type, object) \
131 fwrite (&object, sizeof (object), 1, pdump_out);
133 #define PDUMP_WRITE_ALIGNED(type, object) do { \
134 PDUMP_ALIGN_OUTPUT (type); \
135 PDUMP_WRITE (type, object); \
138 #define PDUMP_READ(ptr, type) \
139 (((type *) (ptr = (char*) (((type *) ptr) + 1)))[-1])
141 #define PDUMP_READ_ALIGNED(ptr, type) \
142 ((ptr = (char *) ALIGN_PTR (ptr, ALIGNOF (type))), PDUMP_READ (ptr, type))
145 const struct lrecord_description *desc;
149 static char *pdump_rt_list = 0;
151 void pdump_objects_unmark(void)
154 char *p = pdump_rt_list;
157 pdump_reloc_table *rt = (pdump_reloc_table *) p;
158 p += sizeof(pdump_reloc_table);
160 for (i = 0; i < rt->count; i++) {
161 struct lrecord_header *lh =
162 *(struct lrecord_header **)p;
163 if (!C_READONLY_RECORD_HEADER_P(lh))
164 UNMARK_RECORD_HEADER(lh);
165 p += sizeof(EMACS_INT);
172 /* The structure of the file
175 stab_offset - nb_root_struct_ptrs*pair(void *, adr)
176 for pointers to structures
177 - nb_opaques*pair(void *, size) for raw bits to restore
179 - root lisp object address/value couples with the count
183 #define PDUMP_SIGNATURE "SXEmacsDP"
184 #define PDUMP_SIGNATURE_LEN (sizeof (PDUMP_SIGNATURE) - 1)
187 char signature[PDUMP_SIGNATURE_LEN];
189 EMACS_UINT stab_offset;
190 EMACS_UINT reloc_address;
191 int nb_root_struct_ptrs;
197 static off_t pdump_length;
200 static void (*pdump_free) (void);
202 static unsigned char pdump_align_table[] = {
203 64, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1,
204 16, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1,
205 32, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1,
206 16, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1
209 static inline unsigned int pdump_size_to_align(size_t size)
211 return pdump_align_table[size % countof(pdump_align_table)];
214 typedef struct pdump_entry_list_elt {
215 struct pdump_entry_list_elt *next;
219 EMACS_INT save_offset;
220 } pdump_entry_list_elt;
223 pdump_entry_list_elt *first;
228 typedef struct pdump_struct_list_elt {
229 pdump_entry_list list;
230 const struct struct_description *sdesc;
231 } pdump_struct_list_elt;
234 pdump_struct_list_elt *list;
239 static pdump_entry_list *pdump_object_table;
240 static pdump_entry_list pdump_opaque_data_list;
241 static pdump_struct_list pdump_struct_table;
243 static int *pdump_alert_undump_object;
245 static unsigned long cur_offset;
246 static size_t max_size;
248 static void *pdump_buf;
249 static FILE *pdump_out;
251 #define PDUMP_HASHSIZE 200001
253 static pdump_entry_list_elt **pdump_hash;
255 /* Since most pointers are eight bytes aligned, the >>3 allows for a better hash */
256 static int pdump_make_hash(const void *obj)
258 return ((unsigned long)(obj) >> 3) % PDUMP_HASHSIZE;
261 static pdump_entry_list_elt *pdump_get_entry(const void *obj)
263 int pos = pdump_make_hash(obj);
264 pdump_entry_list_elt *e;
268 while ((e = pdump_hash[pos]) != 0) {
273 if (pos == PDUMP_HASHSIZE)
280 pdump_add_entry(pdump_entry_list *l, const void *obj, size_t size, int count)
282 pdump_entry_list_elt *e;
283 int pos = pdump_make_hash(obj);
285 while ((e = pdump_hash[pos]) != 0) {
290 if (pos == PDUMP_HASHSIZE) {
295 e = xnew(pdump_entry_list_elt);
307 int align = pdump_size_to_align(size);
309 if (align < l->align) {
316 static pdump_entry_list*
317 pdump_get_entry_list(const struct struct_description *sdesc)
320 for (i = 0; i < pdump_struct_table.count; i++)
321 if (pdump_struct_table.list[i].sdesc == sdesc)
322 return &pdump_struct_table.list[i].list;
324 if (pdump_struct_table.size <= pdump_struct_table.count) {
325 if (pdump_struct_table.size == -1)
326 pdump_struct_table.size = 10;
328 pdump_struct_table.size = pdump_struct_table.size * 2;
329 pdump_struct_table.list = (pdump_struct_list_elt *)
330 xrealloc(pdump_struct_table.list,
331 pdump_struct_table.size *
332 sizeof(pdump_struct_list_elt));
334 pdump_struct_table.list[pdump_struct_table.count].list.first = 0;
335 pdump_struct_table.list[pdump_struct_table.count].list.align =
336 ALIGNOF(max_align_t);
337 pdump_struct_table.list[pdump_struct_table.count].list.count = 0;
338 pdump_struct_table.list[pdump_struct_table.count].sdesc = sdesc;
340 return &pdump_struct_table.list[pdump_struct_table.count++].list;
344 struct lrecord_header *obj;
351 static void pdump_backtrace(void)
354 stderr_out("pdump backtrace :\n");
355 for (i = 0; i < depth; i++) {
356 if (!backtrace[i].obj)
357 stderr_out(" - ind. (%d, %d)\n",
358 backtrace[i].position, backtrace[i].offset);
360 stderr_out(" - %s (%d, %d)\n",
361 LHEADER_IMPLEMENTATION(backtrace[i].obj)->
362 name, backtrace[i].position,
363 backtrace[i].offset);
368 static void pdump_register_object(Lisp_Object obj);
369 static void pdump_register_struct(const void *data,
370 const struct struct_description *sdesc,
374 pdump_get_indirect_count(EMACS_INT code,
375 const struct lrecord_description *idesc,
378 EMACS_INT count = 0; /* initialize to shut up GCC */
381 int line = XD_INDIRECT_VAL(code);
382 int delta = XD_INDIRECT_DELTA(code);
384 irdata = ((const char *)idata) + idesc[line].offset;
385 switch (idesc[line].type) {
387 count = *(const size_t*)irdata;
390 count = *(const int*)irdata;
393 count = *(const long*)irdata;
396 count = *(const Bytecount*)irdata;
399 /* list the rest here */
400 case XD_LISP_OBJECT_ARRAY:
405 case XD_OPAQUE_DATA_PTR:
410 case XD_SPECIFIER_END:
414 ("Unsupported count type : %d (line = %d, code=%ld)\n",
415 idesc[line].type, line, (long)code);
424 pdump_register_sub(const void *data, const struct lrecord_description *desc,
430 for (pos = 0; desc[pos].type != XD_END; pos++) {
431 const void *rdata = (const char *)data + desc[pos].offset;
433 backtrace[me].position = pos;
434 backtrace[me].offset = desc[pos].offset;
436 switch (desc[pos].type) {
437 case XD_SPECIFIER_END:
440 ((const Lisp_Specifier *)data)->methods->
450 case XD_OPAQUE_DATA_PTR: {
451 EMACS_INT count = desc[pos].data1;
452 if (XD_IS_INDIRECT(count)) {
453 count = pdump_get_indirect_count(
456 pdump_add_entry(&pdump_opaque_data_list,
457 *(const void *const*)rdata, count, 1);
461 const char *str = *(const char *const*)rdata;
463 size_t str_sz = strlen(str);
464 pdump_add_entry(&pdump_opaque_data_list,
469 case XD_DOC_STRING: {
470 const char *str = *(const char *const*)rdata;
471 if ((EMACS_INT)str > 0) {
472 pdump_add_entry(&pdump_opaque_data_list,
473 str, strlen(str) + 1, 1);
477 case XD_LISP_OBJECT: {
478 const Lisp_Object *pobj = (const Lisp_Object*)rdata;
480 assert(desc[pos].data1 == 0);
482 backtrace[me].offset =
483 (const char *)pobj - (const char *)data;
484 pdump_register_object(*pobj);
487 case XD_LISP_OBJECT_ARRAY: {
489 EMACS_INT count = desc[pos].data1;
491 if (XD_IS_INDIRECT(count)) {
492 count = pdump_get_indirect_count(
495 for (i = 0; i < count; i++) {
496 const Lisp_Object *pobj =
497 ((const Lisp_Object*)rdata) + i;
498 Lisp_Object dobj = *pobj;
500 backtrace[me].offset =
503 pdump_register_object(dobj);
507 case XD_STRUCT_PTR: {
508 EMACS_INT count = desc[pos].data1;
509 const struct struct_description *sdesc =
511 const char *dobj = *(const char *const*)rdata;
514 if (XD_IS_INDIRECT(count)) {
515 count = pdump_get_indirect_count(
518 pdump_register_struct(dobj, sdesc, count);
526 stderr_out("Unsupported dump type : %d\n",
535 pdump_register_object(Lisp_Object obj)
537 struct lrecord_header *objh;
538 const struct lrecord_implementation *imp;
540 if (!POINTER_TYPE_P(XTYPE(obj))) {
544 objh = XRECORD_LHEADER(obj);
549 if (pdump_get_entry(objh)) {
553 imp = LHEADER_IMPLEMENTATION(objh);
555 if (imp->description) {
558 stderr_out("Backtrace overflow, loop ?\n");
562 backtrace[me].obj = objh;
563 backtrace[me].position = 0;
564 backtrace[me].offset = 0;
566 pdump_add_entry(pdump_object_table + objh->type,
570 imp->size_in_bytes_method(objh), 1);
571 pdump_register_sub(objh, imp->description, me);
574 pdump_alert_undump_object[objh->type]++;
575 stderr_out("Undumpable object type : %s\n", imp->name);
581 pdump_register_struct(const void *data,
582 const struct struct_description *sdesc, int count)
584 if (data && !pdump_get_entry(data)) {
588 stderr_out("Backtrace overflow, loop ?\n");
591 backtrace[me].obj = 0;
592 backtrace[me].position = 0;
593 backtrace[me].offset = 0;
595 pdump_add_entry(pdump_get_entry_list(sdesc),
596 data, sdesc->size, count);
597 for (i = 0; i < count; i++) {
599 ((const char*)data) + sdesc->size * i,
600 sdesc->description, me);
608 pdump_dump_data(pdump_entry_list_elt * elt,
609 const struct lrecord_description *desc)
611 size_t size = elt->size;
612 int count = elt->count;
615 memcpy(pdump_buf, elt->obj, size * count);
617 for (i = 0; i < count; i++) {
618 char *cur = ((char *)pdump_buf) + i * size;
620 for (pos = 0; desc[pos].type != XD_END; pos++) {
621 void *rdata = cur + desc[pos].offset;
622 switch (desc[pos].type) {
623 case XD_SPECIFIER_END:
624 desc = ((const Lisp_Specifier *)
626 methods->extra_description;
634 EMACS_INT val = desc[pos].data1;
635 if (XD_IS_INDIRECT(val))
636 val = pdump_get_indirect_count(
637 val, desc, elt->obj);
641 case XD_OPAQUE_DATA_PTR:
643 case XD_STRUCT_PTR: {
644 void *ptr = *(void**)rdata;
646 *(EMACS_INT*) rdata =
653 Lisp_Object obj = *(Lisp_Object*)rdata;
654 pdump_entry_list_elt *elt1;
657 elt1 = pdump_get_entry(
658 XRECORD_LHEADER(obj));
662 obj = *(Lisp_Object*)(
665 (XRECORD_LHEADER(obj)));
667 *(EMACS_INT *) rdata =
671 case XD_LISP_OBJECT: {
675 assert(desc[pos].data1 == 0);
677 if (POINTER_TYPE_P(XTYPE(*pobj))
678 && XRECORD_LHEADER(*pobj)) {
681 (XRECORD_LHEADER(*pobj))
686 case XD_LISP_OBJECT_ARRAY: {
687 EMACS_INT num = desc[pos].data1;
690 if (XD_IS_INDIRECT(num)) {
691 num = pdump_get_indirect_count(
692 num, desc, elt->obj);
694 for (j = 0; j < num; j++) {
696 ((Lisp_Object*)rdata) +
700 XRECORD_LHEADER(*pobj)) {
711 case XD_DOC_STRING: {
712 EMACS_INT str = *(EMACS_INT*)rdata;
726 ("Unsupported dump type : %d\n",
733 fwrite(desc ? pdump_buf : elt->obj, size, count, pdump_out);
737 pdump_reloc_one(void *data, EMACS_INT delta,
738 const struct lrecord_description *desc)
743 for (pos = 0; desc[pos].type != XD_END; pos++) {
744 void *rdata = (char *)data + desc[pos].offset;
745 switch (desc[pos].type) {
746 case XD_SPECIFIER_END:
748 desc = ((const Lisp_Specifier *)data)->methods->
757 case XD_OPAQUE_DATA_PTR:
761 EMACS_INT ptr = *(EMACS_INT *) rdata;
763 *(EMACS_INT *) rdata = ptr + delta;
767 case XD_LISP_OBJECT: {
768 Lisp_Object *pobj = (Lisp_Object *) rdata;
770 assert(desc[pos].data1 == 0);
772 if (POINTER_TYPE_P(XTYPE(*pobj))
773 && !EQ(*pobj, Qnull_pointer))
775 (char *)XPNTR(*pobj) + delta);
779 case XD_LISP_OBJECT_ARRAY: {
780 EMACS_INT num = desc[pos].data1;
782 if (XD_IS_INDIRECT(num)) {
783 num = pdump_get_indirect_count(num, desc, data);
785 for (j = 0; j < num; j++) {
786 Lisp_Object *pobj = (Lisp_Object*)rdata + j;
788 if (POINTER_TYPE_P(XTYPE(*pobj))
789 && !EQ(*pobj, Qnull_pointer)) {
791 (char *)XPNTR(*pobj) + delta);
796 case XD_DOC_STRING: {
797 EMACS_INT str = *(EMACS_INT *) rdata;
799 *(EMACS_INT *) rdata = str + delta;
807 stderr_out("Unsupported dump type : %d\n",
815 pdump_allocate_offset(pdump_entry_list_elt * elt,
816 const struct lrecord_description *desc)
818 size_t size = elt->count * elt->size;
819 elt->save_offset = cur_offset;
826 pdump_scan_by_alignment(void (*f) (pdump_entry_list_elt *,
827 const struct lrecord_description *))
831 for (align = ALIGNOF(max_align_t); align; align >>= 1) {
833 pdump_entry_list_elt *elt;
835 for (i = 0; i < lrecord_type_count; i++)
836 if (pdump_object_table[i].align == align)
837 for (elt = pdump_object_table[i].first; elt;
840 lrecord_implementations_table[i]->
843 for (i = 0; i < (size_t)pdump_struct_table.count; i++) {
844 pdump_struct_list_elt list = pdump_struct_table.list[i];
845 if (list.list.align == align)
846 for (elt = list.list.first; elt;
848 f(elt, list.sdesc->description);
851 for (elt = pdump_opaque_data_list.first; elt; elt = elt->next)
852 if (pdump_size_to_align(elt->size) ==
858 static void pdump_dump_root_struct_ptrs(void)
861 size_t count = Dynarr_length(pdump_root_struct_ptrs);
862 pdump_static_pointer *data = alloca_array(pdump_static_pointer, count);
863 for (i = 0; i < count; i++) {
865 (char **)Dynarr_atp(pdump_root_struct_ptrs, i)->ptraddress;
867 (char *)pdump_get_entry(*data[i].address)->save_offset;
869 PDUMP_ALIGN_OUTPUT(pdump_static_pointer);
870 fwrite(data, sizeof(pdump_static_pointer), count, pdump_out);
873 static void pdump_dump_opaques(void)
876 for (i = 0; i < Dynarr_length(pdump_opaques); i++) {
877 pdump_opaque *info = Dynarr_atp(pdump_opaques, i);
878 PDUMP_WRITE_ALIGNED(pdump_opaque, *info);
879 fwrite(info->varaddress, info->size, 1, pdump_out);
883 static void pdump_dump_rtables(void)
886 pdump_entry_list_elt *elt;
887 pdump_reloc_table rt;
889 for (i = 0; i < lrecord_type_count; i++) {
890 elt = pdump_object_table[i].first;
893 rt.desc = lrecord_implementations_table[i]->description;
894 rt.count = pdump_object_table[i].count;
895 PDUMP_WRITE_ALIGNED(pdump_reloc_table, rt);
898 pdump_get_entry(elt->obj)->save_offset;
899 PDUMP_WRITE_ALIGNED(EMACS_INT, rdata);
906 PDUMP_WRITE_ALIGNED(pdump_reloc_table, rt);
908 for (i = 0; i < (size_t)pdump_struct_table.count; i++) {
909 elt = pdump_struct_table.list[i].list.first;
910 rt.desc = pdump_struct_table.list[i].sdesc->description;
911 rt.count = pdump_struct_table.list[i].list.count;
912 PDUMP_WRITE_ALIGNED(pdump_reloc_table, rt);
915 pdump_get_entry(elt->obj)->save_offset;
917 for (j = 0; j < elt->count; j++) {
918 PDUMP_WRITE_ALIGNED(EMACS_INT, rdata);
926 PDUMP_WRITE_ALIGNED(pdump_reloc_table, rt);
929 static void pdump_dump_root_objects(void)
931 size_t count = (Dynarr_length(pdump_root_objects) +
932 Dynarr_length(pdump_weak_object_chains));
935 PDUMP_WRITE_ALIGNED(size_t, count);
936 PDUMP_ALIGN_OUTPUT(pdump_static_Lisp_Object);
938 for (i = 0; i < Dynarr_length(pdump_root_objects); i++) {
939 pdump_static_Lisp_Object obj;
940 obj.address = Dynarr_at(pdump_root_objects, i);
941 obj.value = *obj.address;
943 if (POINTER_TYPE_P(XTYPE(obj.value)))
946 pdump_get_entry(XRECORD_LHEADER
950 PDUMP_WRITE(pdump_static_Lisp_Object, obj);
953 for (i = 0; i < Dynarr_length(pdump_weak_object_chains); i++) {
954 pdump_entry_list_elt *elt;
955 pdump_static_Lisp_Object obj;
957 obj.address = Dynarr_at(pdump_weak_object_chains, i);
958 obj.value = *obj.address;
961 const struct lrecord_description *desc;