1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Used to dump structures and unions in forth mode.
31 *
32 * structures and unions are a bit more complicated than enums.  To make things
33 * just that much more interesting, we have to dump the members in reverse
34 * order, which is nice.  But wait!  It gets better!  For compatibility reasons,
35 * we need to dump the members in reverse-offset order, even if member-specific
36 * mode was used to request the members in something other than that order.
37 *
38 * The header op prints the macro header and saves the type being printed.
39 *
40 * In member-specific mode, the member op will be invoked for each structure
41 * or union member.  The member op adds the member name, format, type ID,
42 * and offset to a list, sorted in reverse order by offset.
43 *
44 * The trailer op is called when the structure or enum is complete.  If no
45 * members were specifically requested, then the trailer iterates through all
46 * of the members of the structure, pretending they were.  Each member is thus
47 * added, in reverse-offset order, to the list used in specific-member mode.
48 * Either way, we then proceed through the list, dumping each member out with
49 * fth_print_member.  Structure and union members are printed out differently,
50 * depending on member type, as follows:
51 *
52 *  Integer:
53 *	Normal integers: ' <format> <offset> <type>-field <name>
54 *	  <format> defaults to ".d" for enums, ".x" for others
55 *	  <offset> is the member offset, in bytes.
56 *	  <type> is "byte", "short", "long", or "ext" for 8-, 16-, 32-, and
57 *	    64-bit integers, respectively.
58 *	  <name> is the name of the member being printed
59 *
60 *	Bitfields:	 ' <format> <shift> <mask> <offset> bits-field <name>
61 *	  <format> defaults to ".x"
62 *	  <shift> is the number of times to right-shift the masked value
63 *	  <mask> use to extract the bit-field value from the read value
64 *	  <offset> is the member offset, in bytes
65 *	  <name> is the name of the member being printed
66 *
67 *  Float:		Ignored
68 *
69 *  Pointer:		 ' <format> <offset> ptr-field <name>
70 *	  <format> defaults to .x
71 *	  <offset> is in bytes
72 *	  <name> is the name of the member being printed
73 *
74 *  Array:
75 *	Arrays have a content-type-specific prefix, followed by an array
76 *	suffix.  The resulting line looks like this if the array contents
77 *	type is an integer, a pointer, or an enum:
78 *
79 *			 ' <fldc> ' <fmt> <sz> <elsz> <off> array-field <name>
80 *
81 *	The following is printed for array contents that are arrays:
82 *
83 *			 ' noop ' .x <sz> <elsz> <off> array-field <name>
84 *
85 *	The following is printed for array contents that are structs:
86 *
87 *			 ' noop ' <fmt> <sz> <elsz> <off> array-field <name>
88 *
89 *	  <fldc> is "c@", "w@", "l@", or "x@", depending on whether array
90 *	    elements are 8, 16, 32 or 64 bits wide.
91 *	  <fmt> defaults to ".x"
92 *	  <sz> is the size of the array, in bytes
93 *	  <elsz> is the size of the array elements
94 *	  <off> is the member offset, in bytes
95 *	  <name> is the nam eof the member being printed
96 *
97 *  Struct/Union:	 ' <format> <offset> struct-field <name>
98 *	  <format> defaults to ".x"
99 *	  <offset> is the member offset, in bytes
100 *	  <name> is the name of the member being printed
101 */
102
103#include <stdio.h>
104#include <stdlib.h>
105#include <string.h>
106
107#include "ctf_headers.h"
108#include "forth.h"
109#include "list.h"
110#include "memory.h"
111
112static ctf_id_t	fth_str_curtid;
113static list_t	*fth_str_curmems;
114
115/*
116 * Node type for the member-storage list (fth_str_curmems) built by
117 * fth_struct_members()
118 */
119typedef struct fth_str_mem {
120	char		*fsm_memname;
121	char		*fsm_format;
122	ctf_id_t	fsm_tid;
123	ulong_t		fsm_off;
124} fth_str_mem_t;
125
126typedef struct fth_struct_members_data {
127	char		*fsmd_strname;
128	char		*fsmd_memfilter;
129	char		*fsmd_format;
130	int		fsmd_matched;
131} fth_struct_members_data_t;
132
133static int fth_print_member(fth_str_mem_t *, int);
134
135/* Comparison routined used to insert members into the fth_str_curmems list */
136static int
137fth_struct_memcmp(void *m1, void *m2)
138{
139	fth_str_mem_t *mem1 = m1, *mem2 = m2;
140
141	if (mem1->fsm_off < mem2->fsm_off)
142		return (1);
143	else if (mem1->fsm_off > mem2->fsm_off)
144		return (-1);
145	else
146		return (0);
147}
148
149static void
150fth_free_str_mem(fth_str_mem_t *mem)
151{
152	free(mem->fsm_memname);
153	if (mem->fsm_format)
154		free(mem->fsm_format);
155	free(mem);
156}
157
158static int
159fth_struct_header(ctf_id_t tid)
160{
161	ssize_t sz;
162
163	fth_str_curtid = tid;
164	fth_str_curmems = NULL;
165
166	if ((sz = ctf_type_size(ctf, fth_str_curtid)) == CTF_ERR)
167		return (parse_warn("Can't get size for %s", fth_curtype));
168
169	(void) fprintf(out, "\n");
170	(void) fprintf(out, "vocabulary %s-words\n", fth_curtype);
171	(void) fprintf(out, "h# %x constant %s-sz\n", sz, fth_curtype);
172	(void) fprintf(out, "%x ' %s-words c-struct .%s\n", sz, fth_curtype,
173	    fth_curtype);
174	(void) fprintf(out, "also %s-words definitions\n\n", fth_curtype);
175
176	return (0);
177}
178
179/* Print the array prefix for integer and pointer members */
180static int
181fth_print_level(uint_t bits, char *format)
182{
183	if ((bits & (bits - 1)) != 0 ||(bits % 8) != 0 || bits > 64) {
184		return (parse_warn("Unexpected bit size %d in %s",
185		    bits, fth_curtype));
186	}
187
188	(void) fprintf(out, "' %c@ ' %s", " cw l   x"[bits / 8], format);
189
190	return (0);
191}
192
193/*
194 * Return the format to be used to print the member.  If one of the builtin
195 * formats "d" or "x" were specified, return ".d" or ".x", respectively.
196 * Otherwise, use the user-provided format as is, or use the default if none
197 * was provided.
198 */
199static char *
200fth_convert_format(char *format, char *def)
201{
202	static char dot[3] = ".";
203
204	if (format == NULL)
205		return (def);
206	else if (strlen(format) == 1) {
207		dot[1] = *format;
208		return (dot);
209	} else
210		return (format);
211}
212
213static int
214fth_print_integer(const char *memname, ulong_t off, uint_t bits, char *format,
215    int level)
216{
217	format = fth_convert_format(format, ".x");
218
219	if (bits > 64) {
220		return (parse_warn("%s.%s is too large (>8 bytes)",
221		    fth_curtype, memname));
222	}
223
224	if (level != 0)
225		return (fth_print_level(bits, format));
226
227	if ((bits % NBBY) != 0 || (bits & (bits - 1)) != 0) {
228		/* bit field */
229		uint_t offset, shift, mask;
230
231		offset = (off / 32) * 4;
232		shift = 32 - ((off % 32) + bits);
233		mask = ((1 << bits) - 1) << shift;
234
235		(void) fprintf(out, "' %s %x %x %x bits-field %s\n",
236		    format, shift, mask, offset, memname);
237
238	} else {
239		char *type[] = {
240			NULL, "byte", "short", NULL, "long",
241			NULL, NULL, NULL, "ext"
242		};
243
244		(void) fprintf(out, "' %s %lx %s-field %s\n", format, off / 8,
245		    type[bits / 8], memname);
246	}
247
248	return (0);
249}
250
251static int
252fth_print_pointer(const char *memname, ulong_t off, uint_t bits, char *format,
253    int level)
254{
255	format = fth_convert_format(format, ".x");
256
257	if (level != 0)
258		return (fth_print_level(bits, format));
259
260	(void) fprintf(out, "' %s %lx ptr-field %s\n", format, off / 8,
261	    memname);
262
263	return (0);
264}
265
266static int
267fth_print_struct(char *memname, ulong_t off, char *format,
268    int level)
269{
270	format = fth_convert_format(format, ".x");
271
272	if (level != 0)
273		(void) fprintf(out, "' noop ' %s", format);
274	else {
275		(void) fprintf(out, "' %s %lx struct-field %s\n", format,
276		    off / 8, memname);
277	}
278
279	return (0);
280}
281
282static int
283fth_print_enum(char *memname, ulong_t off, char *format,
284    int level)
285{
286	format = fth_convert_format(format, ".d");
287
288	if (level != 0)
289		(void) fprintf(out, "' l@ ' %s", format);
290	else {
291		(void) fprintf(out, "' %s %lx long-field %s\n", format, off / 8,
292		    memname);
293	}
294
295	return (0);
296}
297
298static int
299fth_print_array(char *memname, ctf_id_t tid, ulong_t off, ssize_t sz,
300    char *format, int level)
301{
302	if (level != 0)
303		(void) fprintf(out, "' noop ' .x");
304	else {
305		fth_str_mem_t mem;
306		ctf_arinfo_t ar;
307
308		/*
309		 * print the prefix for the array contents type, then print
310		 * the array macro
311		 */
312
313		if (ctf_array_info(ctf, tid, &ar) == CTF_ERR) {
314			return (parse_warn("Can't read array in %s.%s",
315			    fth_curtype, memname));
316		}
317
318		mem.fsm_memname = memname;
319		mem.fsm_format = format;
320		mem.fsm_tid = ar.ctr_contents;
321		mem.fsm_off = off;
322
323		if (fth_print_member(&mem, level + 1) < 0)
324			return (-1);
325
326		(void) fprintf(out, " %x %x %lx array-field %s\n", sz,
327		    (sz / ar.ctr_nelems), off / 8, memname);
328	}
329
330	return (0);
331}
332
333/* dump a structure or union member */
334static int
335fth_print_member(fth_str_mem_t *mem, int level)
336{
337	ctf_encoding_t e;
338	ctf_id_t tid;
339	int kind;
340	ssize_t sz;
341
342	if ((tid = ctf_type_resolve(ctf, mem->fsm_tid)) == CTF_ERR) {
343		return (parse_warn("Can't resolve %s.%s", fth_curtype,
344		    mem->fsm_memname));
345	}
346
347	if ((kind = ctf_type_kind(ctf, tid)) == CTF_ERR) {
348		return (parse_warn("Can't get kind for %s.%s",
349		    fth_curtype, mem->fsm_memname));
350	}
351
352	if ((sz = ctf_type_size(ctf, tid)) == CTF_ERR) {
353		return (parse_warn("Can't get size for %s.%s",
354		    fth_curtype, mem->fsm_memname));
355	}
356
357	switch (kind) {
358	case CTF_K_INTEGER:
359		if (ctf_type_encoding(ctf, tid, &e) == CTF_ERR)
360			return (parse_warn("Can't get encoding for %ld", tid));
361
362		return (fth_print_integer(mem->fsm_memname, mem->fsm_off,
363		    e.cte_bits, mem->fsm_format, level));
364
365	case CTF_K_FLOAT:
366		(void) parse_warn("Ignoring floating point member %s.%s",
367		    fth_curtype, mem->fsm_memname);
368		return (0);
369
370	case CTF_K_POINTER:
371		return (fth_print_pointer(mem->fsm_memname, mem->fsm_off,
372		    sz * 8, mem->fsm_format, level));
373
374	case CTF_K_ARRAY:
375		return (fth_print_array(mem->fsm_memname, tid, mem->fsm_off, sz,
376		    mem->fsm_format, level));
377
378	case CTF_K_STRUCT:
379	case CTF_K_UNION:
380		return (fth_print_struct(mem->fsm_memname, mem->fsm_off,
381		    mem->fsm_format, level));
382
383	case CTF_K_ENUM:
384		return (fth_print_enum(mem->fsm_memname, mem->fsm_off,
385		    mem->fsm_format, level));
386
387	case CTF_K_FORWARD:
388		return (parse_warn("Type %ld in %s.%s is undefined", tid,
389		    fth_curtype, mem->fsm_memname));
390
391	default:
392		return (parse_warn("Unexpected kind %d for %s.%s", kind,
393		    fth_curtype, mem->fsm_memname));
394	}
395}
396
397/*
398 * Add a member to list of members to be printed (fth_str_curmems).  If
399 * fsmd_memfilter is non-null, only add this member if its name matches that
400 * in the filter.
401 */
402static int
403fth_struct_members_cb(const char *memname, ctf_id_t tid, ulong_t off, void *arg)
404{
405	fth_struct_members_data_t *fsmd = arg;
406	fth_str_mem_t *mem;
407
408	if (fsmd->fsmd_memfilter != NULL && strcmp(fsmd->fsmd_memfilter,
409	    memname) != 0)
410		return (0);
411
412	fsmd->fsmd_matched = 1;
413
414	mem = xcalloc(sizeof (fth_str_mem_t));
415	mem->fsm_memname = xstrdup(memname);
416	if (fsmd->fsmd_format)
417		mem->fsm_format = xstrdup(fsmd->fsmd_format);
418	mem->fsm_tid = tid;
419	mem->fsm_off = off;
420
421	slist_add(&fth_str_curmems, mem, fth_struct_memcmp);
422
423	return (0);
424}
425
426/*
427 * If memfilter is non-null, iterate through the members of this type, causing
428 * every member to be added to the list.  Otherwise, use the iterator and
429 * the callback to add only the specified member.
430 */
431static int
432fth_struct_members(char *memfilter, char *format)
433{
434	fth_struct_members_data_t fsmd;
435
436	fsmd.fsmd_strname = fth_curtype;
437	fsmd.fsmd_memfilter = memfilter;
438	fsmd.fsmd_format = format;
439	fsmd.fsmd_matched = 0;
440
441	if (ctf_member_iter(ctf, fth_str_curtid, fth_struct_members_cb,
442	    &fsmd) != 0)
443		return (-1);
444
445	if (memfilter != NULL && fsmd.fsmd_matched == 0) {
446		return (parse_warn("Invalid member %s.%s", fth_curtype,
447		    memfilter));
448	}
449
450	return (0);
451}
452
453static int
454fth_struct_trailer(void)
455{
456	if (list_count(fth_str_curmems) == 0) {
457		if (fth_struct_members(NULL, NULL) < 0)
458			return (-1);
459	}
460
461	while (!list_empty(fth_str_curmems)) {
462		fth_str_mem_t *mem = list_remove(&fth_str_curmems,
463		    list_first(fth_str_curmems), NULL, NULL);
464
465		if (fth_print_member(mem, 0) < 0)
466			return (-1);
467
468		fth_free_str_mem(mem);
469	}
470
471	(void) fprintf(out, "\n");
472	(void) fprintf(out, "kdbg-words definitions\n");
473	(void) fprintf(out, "previous\n");
474	(void) fprintf(out, "\n");
475	(void) fprintf(out, "\\ end %s section\n", fth_curtype);
476	(void) fprintf(out, "\n");
477
478	return (0);
479}
480
481fth_type_ops_t fth_struct_ops = {
482	fth_struct_header,
483	fth_struct_members,
484	fth_struct_trailer
485};
486