aboutsummaryrefslogtreecommitdiffstats
path: root/ctf_encoder.c
blob: b761287d45348c59f8cd468292d287c65c90d32c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
  SPDX-License-Identifier: GPL-2.0-only

  Copyright (C) 2009 Red Hat Inc.
  Copyright (C) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
*/

#include "dwarves.h"
#include "libctf.h"
#include "ctf.h"
#include "hash.h"
#include "elf_symtab.h"
#include <inttypes.h>

static int tag__check_id_drift(const struct tag *tag,
			       uint32_t core_id, uint32_t ctf_id)
{
	if (ctf_id != core_id) {
		fprintf(stderr, "%s: %s id drift, core: %u, libctf: %d\n",
			__func__, dwarf_tag_name(tag->tag), core_id, ctf_id);
		return -1;
	}
	return 0;
}

static int dwarf_to_ctf_type(uint16_t tag)
{
	switch (tag) {
	case DW_TAG_const_type:		return CTF_TYPE_KIND_CONST;
	case DW_TAG_pointer_type:	return CTF_TYPE_KIND_PTR;
	case DW_TAG_restrict_type:	return CTF_TYPE_KIND_RESTRICT;
	case DW_TAG_volatile_type:	return CTF_TYPE_KIND_VOLATILE;
	case DW_TAG_class_type:
	case DW_TAG_structure_type:	return CTF_TYPE_KIND_STR;
	case DW_TAG_union_type:		return CTF_TYPE_KIND_UNION;
	}
	return 0xffff;
}

static int base_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	struct base_type *bt = tag__base_type(tag);
	uint32_t ctf_id = ctf__add_base_type(ctf, bt->name, bt->bit_size);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	return 0;
}

static int pointer_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	uint32_t ctf_id = ctf__add_short_type(ctf, dwarf_to_ctf_type(tag->tag), tag->type, 0);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	return 0;
}

static int typedef__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	uint32_t ctf_id = ctf__add_short_type(ctf, CTF_TYPE_KIND_TYPDEF, tag->type, tag__namespace(tag)->name);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	return 0;
}

static int fwd_decl__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	uint32_t ctf_id = ctf__add_fwd_decl(ctf, tag__namespace(tag)->name);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	return 0;
}

static int structure_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	struct type *type = tag__type(tag);
	int64_t position;
	uint32_t ctf_id = ctf__add_struct(ctf, dwarf_to_ctf_type(tag->tag),
				     type->namespace.name, type->size,
				     type->nr_members, &position);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	const bool is_short = type->size < CTF_SHORT_MEMBER_LIMIT;
	struct class_member *pos;
	type__for_each_data_member(type, pos) {
		if (is_short)
			ctf__add_short_member(ctf, pos->name, pos->tag.type,
					      pos->bit_offset, &position);
		else
			ctf__add_full_member(ctf, pos->name, pos->tag.type,
					     pos->bit_offset, &position);
	}

	return 0;
}

static uint32_t array_type__nelems(struct tag *tag)
{
	int i;
	uint32_t nelem = 1;
	struct array_type *array = tag__array_type(tag);

	for (i = array->dimensions - 1; i >= 0; --i)
		nelem *= array->nr_entries[i];

	return nelem;
}

static int array_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	const uint32_t nelems = array_type__nelems(tag);
	uint32_t ctf_id = ctf__add_array(ctf, tag->type, 0, nelems);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	return 0;
}

static int subroutine_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	struct parameter *pos;
	int64_t position;
	struct ftype *ftype = tag__ftype(tag);
	uint32_t ctf_id = ctf__add_function_type(ctf, tag->type, ftype->nr_parms, ftype->unspec_parms, &position);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	ftype__for_each_parameter(ftype, pos)
		ctf__add_parameter(ctf, pos->tag.type, &position);

	return 0;
}

static int enumeration_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	struct type *etype = tag__type(tag);
	int64_t position;
	uint32_t ctf_id = ctf__add_enumeration_type(ctf, etype->namespace.name,
					       etype->size, etype->nr_members,
					       &position);

	if (tag__check_id_drift(tag, core_id, ctf_id))
		return -1;

	struct enumerator *pos;
	type__for_each_enumerator(etype, pos)
		ctf__add_enumerator(ctf, pos->name, pos->value, &position);

	return 0;
}

static void tag__encode_ctf(struct tag *tag, uint32_t core_id, struct ctf *ctf)
{
	switch (tag->tag) {
	case DW_TAG_base_type:
		base_type__encode(tag, core_id, ctf);
		break;
	case DW_TAG_const_type:
	case DW_TAG_pointer_type:
	case DW_TAG_restrict_type:
	case DW_TAG_volatile_type:
		pointer_type__encode(tag, core_id, ctf);
		break;
	case DW_TAG_typedef:
		typedef__encode(tag, core_id, ctf);
		break;
	case DW_TAG_structure_type:
	case DW_TAG_union_type:
	case DW_TAG_class_type:
		if (tag__type(tag)->declaration)
			fwd_decl__encode(tag, core_id, ctf);
		else
			structure_type__encode(tag, core_id, ctf);
		break;
	case DW_TAG_array_type:
		array_type__encode(tag, core_id, ctf);
		break;
	case DW_TAG_subroutine_type:
		subroutine_type__encode(tag, core_id, ctf);
		break;
	case DW_TAG_enumeration_type:
		enumeration_type__encode(tag, core_id, ctf);
		break;
	}
}

#define HASHADDR__BITS 8
#define HASHADDR__SIZE (1UL << HASHADDR__BITS)
#define hashaddr__fn(key) hash_64(key, HASHADDR__BITS)

static struct function *hashaddr__find_function(const struct hlist_head hashtable[],
						const uint64_t addr)
{
	struct function *function;
	struct hlist_node *pos;
	uint16_t bucket = hashaddr__fn(addr);
	const struct hlist_head *head = &hashtable[bucket];

	hlist_for_each_entry(function, pos, head, tool_hnode) {
		if (function->lexblock.ip.addr == addr)
			return function;
	}

	return NULL;
}

static struct variable *hashaddr__find_variable(const struct hlist_head hashtable[],
						const uint64_t addr)
{
	struct variable *variable;
	struct hlist_node *pos;
	uint16_t bucket = hashaddr__fn(addr);
	const struct hlist_head *head = &hashtable[bucket];

	hlist_for_each_entry(variable, pos, head, tool_hnode) {
		if (variable->ip.addr == addr)
			return variable;
	}

	return NULL;
}

/*
 * FIXME: Its in the DWARF loader, we have to find a better handoff
 * mechanizm...
 */
extern struct strings *strings;

int cu__encode_ctf(struct cu *cu, int verbose)
{
	int err = -1;
	struct ctf *ctf = ctf__new(cu->filename, cu->elf);

	if (ctf == NULL)
		goto out;

	if (cu__cache_symtab(cu) < 0)
		goto out_delete;

	ctf__set_strings(ctf, strings);

	uint32_t id;
	struct tag *pos;
	cu__for_each_type(cu, id, pos)
		tag__encode_ctf(pos, id, ctf);

	struct hlist_head hash_addr[HASHADDR__SIZE];

	for (id = 0; id < HASHADDR__SIZE; ++id)
		INIT_HLIST_HEAD(&hash_addr[id]);

	struct function *function;
	cu__for_each_function(cu, id, function) {
		uint64_t addr = function->lexblock.ip.addr;
		struct hlist_head *head = &hash_addr[hashaddr__fn(addr)];
		hlist_add_head(&function->tool_hnode, head);
	}

	uint64_t addr;
	GElf_Sym sym;
	const char *sym_name;
	cu__for_each_cached_symtab_entry(cu, id, sym, sym_name) {
		if (ctf__ignore_symtab_function(&sym, sym_name))
			continue;

		addr = elf_sym__value(&sym);
		int64_t position;
		function = hashaddr__find_function(hash_addr, addr);
		if (function == NULL) {
			if (verbose)
				fprintf(stderr,
					"function %4d: %-20s %#" PRIx64 " %5u NOT FOUND!\n",
					id, sym_name, addr,
					elf_sym__size(&sym));
			err = ctf__add_function(ctf, 0, 0, 0, &position);
			if (err != 0)
				goto out_err_ctf;
			continue;
		}

		const struct ftype *ftype = &function->proto;
		err = ctf__add_function(ctf, function->proto.tag.type,
					ftype->nr_parms,
					ftype->unspec_parms, &position);

		if (err != 0)
			goto out_err_ctf;

		struct parameter *pos;
		ftype__for_each_parameter(ftype, pos)
			ctf__add_function_parameter(ctf, pos->tag.type, &position);
	}

	for (id = 0; id < HASHADDR__SIZE; ++id)
		INIT_HLIST_HEAD(&hash_addr[id]);

	struct variable *var;
	cu__for_each_variable(cu, id, pos) {
		var = tag__variable(pos);
		if (variable__scope(var) != VSCOPE_GLOBAL)
			continue;
		struct hlist_head *head = &hash_addr[hashaddr__fn(var->ip.addr)];
		hlist_add_head(&var->tool_hnode, head);
	}

	cu__for_each_cached_symtab_entry(cu, id, sym, sym_name) {
		if (ctf__ignore_symtab_object(&sym, sym_name))
			continue;
		addr = elf_sym__value(&sym);

		var = hashaddr__find_variable(hash_addr, addr);
		if (var == NULL) {
			if (verbose)
				fprintf(stderr,
					"variable %4d: %-20s %#" PRIx64 " %5u NOT FOUND!\n",
					id, sym_name, addr,
					elf_sym__size(&sym));
			err = ctf__add_object(ctf, 0);
			if (err != 0)
				goto out_err_ctf;
			continue;
		}

		err = ctf__add_object(ctf, var->ip.tag.type);
		if (err != 0)
			goto out_err_ctf;
	}

	ctf__encode(ctf, CTF_FLAGS_COMPR);

	err = 0;
out_delete:
	ctf__delete(ctf);
out:
	return err;
out_err_ctf:
	fprintf(stderr,
		"%4d: %-20s %#llx %5u failed encoding, "
		"ABORTING!\n", id, sym_name,
		(unsigned long long)addr, elf_sym__size(&sym));
	goto out_delete;
}