pahole: Support multiple types for pretty printing
For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
78f2177d90
commit
22f93766cf
|
@ -896,6 +896,16 @@ $ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset'
|
||||||
},
|
},
|
||||||
$
|
$
|
||||||
.fi
|
.fi
|
||||||
|
.P
|
||||||
|
It is possible to pass multiple types, one has only to make sure they appear in the file
|
||||||
|
in sequence, i.e. for the perf.data example, see the perf_file_header dump above, one can print
|
||||||
|
the perf_file_attr structs in the header attrs range, then the perf_event_header in the
|
||||||
|
data range with the following command:
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
pahole ~/bin/perf --header=perf_file_header \
|
||||||
|
-C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data
|
||||||
|
.fi
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
\fIeu-readelf\fR(1), \fIreadelf\fR(1), \fIobjdump\fR(1).
|
\fIeu-readelf\fR(1), \fIreadelf\fR(1), \fIobjdump\fR(1).
|
||||||
|
|
297
pahole.c
297
pahole.c
|
@ -1603,46 +1603,6 @@ static bool type__filter_value(struct tag *tag, void *instance)
|
||||||
return value != filter->right;
|
return value != filter->right;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *cus__tag_real_type(struct cus *cus, struct tag *tag, struct cu **cup, void *instance)
|
|
||||||
{
|
|
||||||
if (tag__is_struct(tag)) {
|
|
||||||
struct type *type = tag__type(tag);
|
|
||||||
|
|
||||||
if (!list_empty(&type->type_enum) && type->type_member) {
|
|
||||||
struct class_member *member = type->type_member;
|
|
||||||
uint64_t value = base_type__value(instance + member->byte_offset, member->byte_size);
|
|
||||||
struct cu *cu_enumerator;
|
|
||||||
struct enumerator *enumerator = enumerations__lookup_entry_from_value(&type->type_enum, &cu_enumerator, value);
|
|
||||||
char name[1024];
|
|
||||||
|
|
||||||
if (!enumerator)
|
|
||||||
return tag;
|
|
||||||
|
|
||||||
if (enumerator->type_enum.tag) {
|
|
||||||
*cup = enumerator->type_enum.cu;
|
|
||||||
return enumerator->type_enum.tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(name, sizeof(name), enumerator__name(enumerator, cu_enumerator));
|
|
||||||
strlwr(name);
|
|
||||||
|
|
||||||
struct cu *orig_cu = *cup;
|
|
||||||
struct tag *real_type = cus__find_type_by_name(cus, cup, name, false, NULL);
|
|
||||||
|
|
||||||
if (real_type && tag__is_struct(real_type)) {
|
|
||||||
enumerator->type_enum.tag = real_type;
|
|
||||||
enumerator->type_enum.cu = *cup;
|
|
||||||
return real_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the cast operation wasn't done, restore the original CU
|
|
||||||
*cup = orig_cu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct tag *tag__real_type(struct tag *tag, struct cu **cup, void *instance)
|
static struct tag *tag__real_type(struct tag *tag, struct cu **cup, void *instance)
|
||||||
{
|
{
|
||||||
if (tag__is_struct(tag)) {
|
if (tag__is_struct(tag)) {
|
||||||
|
@ -1786,50 +1746,54 @@ static int64_t type__instance_read_once(struct type_instance *instance, FILE *fp
|
||||||
* @name - type name
|
* @name - type name
|
||||||
* @type - name of the member containing a type id
|
* @type - name of the member containing a type id
|
||||||
* @type_enum - translate @type into a enum entry/string
|
* @type_enum - translate @type into a enum entry/string
|
||||||
|
* @type_enum_resolved - if this was already resolved, i.e. if the enums were find in some CU
|
||||||
* @size - the member with the size for variable sized records
|
* @size - the member with the size for variable sized records
|
||||||
* @filter - filter expression using record contents and values or enum entries
|
* @filter - filter expression using record contents and values or enum entries
|
||||||
* @range - from where to get seek_bytes and size_bytes where to pretty print this specific class
|
* @range - from where to get seek_bytes and size_bytes where to pretty print this specific class
|
||||||
*/
|
*/
|
||||||
struct prototype {
|
struct prototype {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
struct tag *class;
|
||||||
|
struct cu *cu;
|
||||||
const char *type,
|
const char *type,
|
||||||
*type_enum,
|
*type_enum,
|
||||||
*size,
|
*size,
|
||||||
*range;
|
*range;
|
||||||
char *filter;
|
char *filter;
|
||||||
uint16_t nr_args;
|
uint16_t nr_args;
|
||||||
|
bool type_enum_resolved;
|
||||||
char name[0];
|
char name[0];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tag__stdio_fprintf_value(struct tag *type, struct prototype *prototype,
|
static int prototype__stdio_fprintf_value(struct prototype *prototype, struct type_instance *header, FILE *fp)
|
||||||
struct cu *cu, struct cus *cus, struct type_instance *header, FILE *fp)
|
|
||||||
{
|
{
|
||||||
|
struct tag *type = prototype->class;
|
||||||
|
struct cu *cu = prototype->cu;
|
||||||
int _sizeof = tag__size(type, cu), printed = 0;
|
int _sizeof = tag__size(type, cu), printed = 0;
|
||||||
int max_sizeof = _sizeof;
|
int max_sizeof = _sizeof;
|
||||||
void *instance = malloc(_sizeof);
|
void *instance = malloc(_sizeof);
|
||||||
uint64_t size_bytes = ULLONG_MAX;
|
uint64_t size_bytes = ULLONG_MAX;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
uint32_t skip = conf.skip;
|
uint32_t skip = conf.skip;
|
||||||
static uint64_t total_read_bytes = 0;
|
|
||||||
|
|
||||||
if (instance == NULL)
|
if (instance == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
off_t header_size = type__instance_read_once(header, stdin);
|
if (type__instance_read_once(header, stdin) < 0) {
|
||||||
if (header_size < 0) {
|
|
||||||
int err = --errno;
|
int err = --errno;
|
||||||
fprintf(stderr, "pahole: --header (%s) type not be read\n", conf.header_type);
|
fprintf(stderr, "pahole: --header (%s) type not be read\n", conf.header_type);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
total_read_bytes += header_size;
|
|
||||||
|
|
||||||
if (conf.range || prototype->range) {
|
if (conf.range || prototype->range) {
|
||||||
off_t seek_bytes;
|
off_t seek_bytes;
|
||||||
const char *range = conf.range ?: prototype->range;
|
const char *range = conf.range ?: prototype->range;
|
||||||
|
|
||||||
if (!header) {
|
if (!header) {
|
||||||
|
if (conf.header_type)
|
||||||
|
fprintf(stderr, "pahole: --header_type=%s not found\n", conf.header_type);
|
||||||
|
else
|
||||||
fprintf(stderr, "pahole: range (%s) requires --header\n", range);
|
fprintf(stderr, "pahole: range (%s) requires --header\n", range);
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
}
|
}
|
||||||
|
@ -1854,6 +1818,8 @@ static int tag__stdio_fprintf_value(struct tag *type, struct prototype *prototyp
|
||||||
|
|
||||||
free(member_name);
|
free(member_name);
|
||||||
|
|
||||||
|
off_t total_read_bytes = ftell(stdin);
|
||||||
|
|
||||||
// Since we're reading stdin, we need to account for what we already read
|
// Since we're reading stdin, we need to account for what we already read
|
||||||
if (seek_bytes < total_read_bytes) {
|
if (seek_bytes < total_read_bytes) {
|
||||||
fprintf(stderr, "pahole: can't go back in stdin, already read %" PRIu64 " bytes, can't go to position %ld\n",
|
fprintf(stderr, "pahole: can't go back in stdin, already read %" PRIu64 " bytes, can't go to position %ld\n",
|
||||||
|
@ -1935,7 +1901,7 @@ static int tag__stdio_fprintf_value(struct tag *type, struct prototype *prototyp
|
||||||
|
|
||||||
if (header) {
|
if (header) {
|
||||||
// Since we're reading stdin, we need to account for already read header:
|
// Since we're reading stdin, we need to account for already read header:
|
||||||
seek_bytes -= header->type->size;
|
seek_bytes -= ftell(stdin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pipe_seek(stdin, seek_bytes) < 0) {
|
if (pipe_seek(stdin, seek_bytes) < 0) {
|
||||||
|
@ -1973,6 +1939,7 @@ static int tag__stdio_fprintf_value(struct tag *type, struct prototype *prototyp
|
||||||
do_read:
|
do_read:
|
||||||
{
|
{
|
||||||
uint64_t read_bytes = 0;
|
uint64_t read_bytes = 0;
|
||||||
|
off_t record_offset = ftell(stdin);
|
||||||
|
|
||||||
while (fread(instance, _sizeof, 1, stdin) == 1) {
|
while (fread(instance, _sizeof, 1, stdin) == 1) {
|
||||||
// Read it from each record/instance
|
// Read it from each record/instance
|
||||||
|
@ -1997,7 +1964,6 @@ do_read:
|
||||||
}
|
}
|
||||||
|
|
||||||
read_bytes += real_sizeof;
|
read_bytes += real_sizeof;
|
||||||
total_read_bytes += real_sizeof;
|
|
||||||
|
|
||||||
if (tag__type(type)->filter && type__filter_value(type, instance))
|
if (tag__type(type)->filter && type__filter_value(type, instance))
|
||||||
goto next_record;
|
goto next_record;
|
||||||
|
@ -2051,36 +2017,18 @@ do_read:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct cu *real_type_cu = cu;
|
struct cu *real_type_cu = cu;
|
||||||
/*
|
|
||||||
* First look at the same CU, then, if cus was passed,
|
|
||||||
* everywhere.
|
|
||||||
*
|
|
||||||
* This is because we can go from, say, 'struct
|
|
||||||
* perf_event_header' to 'struct perf_record_mmap2', the later
|
|
||||||
* having a 'struct perf_event_header' as its first member
|
|
||||||
* would not find the type->type_enum set if the CU where
|
|
||||||
* perf_record_mmap2 is found is different than the one where
|
|
||||||
* the 'type' instance for 'perf_event_header' was first found.
|
|
||||||
*
|
|
||||||
* Perhaps we should do a best effort in the fallback, i.e.
|
|
||||||
* when we're looking at the list of classes that were not
|
|
||||||
* processed by the stealer, i.e. in the cus__load_files()
|
|
||||||
* call, when we must use cus__find to locate all the types
|
|
||||||
* referenced in the 'struct prototype' members. Yeah, dealing
|
|
||||||
* with DWARF and its multiple representations for types found
|
|
||||||
* in each of its CUs complicates things.
|
|
||||||
*
|
|
||||||
* When using BTF this is all moot, as we have just one 'CU'
|
|
||||||
* with all the types. The fallbacks only happen when
|
|
||||||
* referenced types are _really_ not found, and in the real_type
|
|
||||||
* case we just pretty print with the original type and skip
|
|
||||||
* to the next record using its ->sizeof field.
|
|
||||||
*/
|
|
||||||
struct tag *real_type = tag__real_type(type, &real_type_cu, instance);
|
struct tag *real_type = tag__real_type(type, &real_type_cu, instance);
|
||||||
|
|
||||||
if (real_type == NULL)
|
if (real_type == NULL)
|
||||||
real_type = cus ? cus__tag_real_type(cus, type, &real_type_cu, instance) : type;
|
real_type = type;
|
||||||
|
|
||||||
|
if (global_verbose) {
|
||||||
|
printed += fprintf(fp, "// type=%s, offset=%#" PRIx64 ", sizeof=%d", type__name(tag__type(type), cu), record_offset, _sizeof);
|
||||||
|
if (real_sizeof != _sizeof)
|
||||||
|
printed += fprintf(fp, ", real_sizeof=%d\n", real_sizeof);
|
||||||
|
else
|
||||||
|
printed += fprintf(fp, "\n");
|
||||||
|
}
|
||||||
printed += tag__fprintf_value(real_type, real_type_cu, instance, real_sizeof, fp);
|
printed += tag__fprintf_value(real_type, real_type_cu, instance, real_sizeof, fp);
|
||||||
printed += fprintf(fp, ",\n");
|
printed += fprintf(fp, ",\n");
|
||||||
|
|
||||||
|
@ -2089,6 +2037,8 @@ do_read:
|
||||||
next_record:
|
next_record:
|
||||||
if (read_bytes >= size_bytes)
|
if (read_bytes >= size_bytes)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
record_offset = ftell(stdin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
@ -2396,7 +2346,7 @@ static struct type_instance *header;
|
||||||
static enum load_steal_kind pahole_stealer(struct cu *cu,
|
static enum load_steal_kind pahole_stealer(struct cu *cu,
|
||||||
struct conf_load *conf_load __unused)
|
struct conf_load *conf_load __unused)
|
||||||
{
|
{
|
||||||
int ret = !isatty(0) ? LSK__KEEPIT : LSK__DELETE;
|
int ret = LSK__DELETE;
|
||||||
|
|
||||||
if (!cu__filter(cu))
|
if (!cu__filter(cu))
|
||||||
goto filter_it;
|
goto filter_it;
|
||||||
|
@ -2431,19 +2381,27 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
|
||||||
|
|
||||||
if (header == NULL && conf.header_type) {
|
if (header == NULL && conf.header_type) {
|
||||||
header = type_instance__new(tag__type(cu__find_type_by_name(cu, conf.header_type, false, NULL)), cu);
|
header = type_instance__new(tag__type(cu__find_type_by_name(cu, conf.header_type, false, NULL)), cu);
|
||||||
if (!header) // Lets try another CU where we can find the header type
|
if (header)
|
||||||
return LSK__KEEPIT;
|
ret = LSK__KEEPIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool include_decls = find_pointers_in_structs != 0 || stats_formatter == nr_methods_formatter;
|
bool include_decls = find_pointers_in_structs != 0 || stats_formatter == nr_methods_formatter;
|
||||||
struct prototype *prototype, *n;
|
struct prototype *prototype, *n;
|
||||||
|
|
||||||
list_for_each_entry_safe(prototype, n, &class_names, node) {
|
list_for_each_entry_safe(prototype, n, &class_names, node) {
|
||||||
|
|
||||||
|
/* See if we already found it */
|
||||||
|
if (prototype->class) {
|
||||||
|
if (prototype->type_enum && !prototype->type_enum_resolved)
|
||||||
|
prototype->type_enum_resolved = type__find_type_enum(tag__type(prototype->class), cu, prototype->type_enum) == 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
static type_id_t class_id;
|
static type_id_t class_id;
|
||||||
struct tag *class = cu__find_type_by_name(cu, prototype->name, include_decls, &class_id);
|
struct tag *class = cu__find_type_by_name(cu, prototype->name, include_decls, &class_id);
|
||||||
|
|
||||||
if (class == NULL)
|
if (class == NULL)
|
||||||
continue; // couldn't find that class name in this CU, continue to the next one.
|
return ret; // couldn't find that class name in this CU, continue to the next one.
|
||||||
|
|
||||||
if (prototype->nr_args != 0 && !tag__is_struct(class)) {
|
if (prototype->nr_args != 0 && !tag__is_struct(class)) {
|
||||||
fprintf(stderr, "pahole: attributes are only supported with 'class' and 'struct' types\n");
|
fprintf(stderr, "pahole: attributes are only supported with 'class' and 'struct' types\n");
|
||||||
|
@ -2471,10 +2429,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototype->type_enum) {
|
if (prototype->type_enum) {
|
||||||
if (type__find_type_enum(type, cu, prototype->type_enum)) {
|
prototype->type_enum_resolved = type__find_type_enum(type, cu, prototype->type_enum) == 0;
|
||||||
// just continue, maybe we'll find a CU that has all the types, if not, at the end we'll do multi-CU searches
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototype->filter) {
|
if (prototype->filter) {
|
||||||
|
@ -2492,21 +2447,18 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
|
||||||
class_id = 0;
|
class_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isatty(0)) {
|
||||||
|
prototype->class = class;
|
||||||
|
prototype->cu = cu;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ok, found it, so remove from the list to avoid printing it
|
* Ok, found it, so remove from the list to avoid printing it
|
||||||
* twice, in another CU.
|
* twice, in another CU.
|
||||||
*/
|
*/
|
||||||
list_del_init(&prototype->node);
|
list_del_init(&prototype->node);
|
||||||
|
|
||||||
if (!isatty(0)) {
|
|
||||||
/*
|
|
||||||
* For the pretty printer only the first class is considered,
|
|
||||||
* ignore the rest.
|
|
||||||
*/
|
|
||||||
tag__stdio_fprintf_value(class, prototype, cu, NULL, header, stdout);
|
|
||||||
return LSK__STOP_LOADING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defined_in) {
|
if (defined_in) {
|
||||||
puts(cu->name);
|
puts(cu->name);
|
||||||
goto dump_it;
|
goto dump_it;
|
||||||
|
@ -2531,6 +2483,25 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we got here with pretty printing is because we have everything solved except for type_enum
|
||||||
|
|
||||||
|
if (!isatty(0)) {
|
||||||
|
// Check if we need to continue loading CUs to get those type_enum= resolved
|
||||||
|
list_for_each_entry(prototype, &class_names, node) {
|
||||||
|
if (prototype->type_enum && !prototype->type_enum_resolved)
|
||||||
|
return LSK__KEEPIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All set, pretty print it!
|
||||||
|
list_for_each_entry_safe(prototype, n, &class_names, node) {
|
||||||
|
list_del_init(&prototype->node);
|
||||||
|
if (prototype__stdio_fprintf_value(prototype, header, stdout) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LSK__STOP_LOADING;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we found all the entries in --class_name, stop
|
* If we found all the entries in --class_name, stop
|
||||||
*/
|
*/
|
||||||
|
@ -2663,54 +2634,6 @@ out_free:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cus__find_type_enum(struct cus *cus, struct type *type, const char *type_enum)
|
|
||||||
{
|
|
||||||
struct cu *cu;
|
|
||||||
struct tag *te = cus__find_type_by_name(cus, &cu, type_enum, false, NULL);
|
|
||||||
|
|
||||||
if (te)
|
|
||||||
return type__add_type_enum(type, te, cu);
|
|
||||||
|
|
||||||
// Now look at a 'virtual enum', i.e. the concatenation of multiple enums
|
|
||||||
char *sep = strchr(type_enum, '+');
|
|
||||||
|
|
||||||
if (!sep)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
char *type_enums = strdup(type_enum);
|
|
||||||
|
|
||||||
if (!type_enums)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
sep = type_enums + (sep - type_enum);
|
|
||||||
|
|
||||||
type_enum = type_enums;
|
|
||||||
*sep = '\0';
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
te = cus__find_type_by_name(cus, &cu, type_enum, false, NULL);
|
|
||||||
|
|
||||||
if (!te)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
ret = type__add_type_enum(type, te, cu);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (sep == NULL)
|
|
||||||
break;
|
|
||||||
type_enum = sep + 1;
|
|
||||||
sep = strchr(type_enum, '+');
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
out:
|
|
||||||
free(type_enums);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int err, remaining, rc = EXIT_FAILURE;
|
int err, remaining, rc = EXIT_FAILURE;
|
||||||
|
@ -2762,78 +2685,34 @@ try_sole_arg_as_class_names:
|
||||||
|
|
||||||
if (!list_empty(&class_names)) {
|
if (!list_empty(&class_names)) {
|
||||||
struct prototype *prototype;
|
struct prototype *prototype;
|
||||||
struct tag *class;
|
|
||||||
struct cu *cu;
|
|
||||||
|
|
||||||
if (conf.header_type) {
|
|
||||||
class = cus__find_type_by_name(cus, &cu, conf.header_type, false, NULL);
|
|
||||||
if (!class) {
|
|
||||||
fprintf(stderr, "pahole: --header (%s) type not found\n", conf.header_type);
|
|
||||||
goto out_cus_delete;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!header) {
|
|
||||||
header = type_instance__new(tag__type(class), cu);
|
|
||||||
if (!header) {
|
|
||||||
fprintf(stderr, "pahole: not enough memory for --header (%s) type\n", conf.header_type);
|
|
||||||
goto out_cus_delete;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each_entry(prototype, &class_names, node) {
|
list_for_each_entry(prototype, &class_names, node) {
|
||||||
if (prototype->nr_args == 0)
|
if (prototype->class == NULL) {
|
||||||
goto not_found_continue;
|
fprintf(stderr, "pahole: type '%s' not found%s\n", prototype->name,
|
||||||
|
prototype->nr_args ? " or arguments not validated" : "");
|
||||||
class = cus__find_type_by_name(cus, &cu, prototype->name, false, NULL);
|
|
||||||
if (!class) {
|
|
||||||
not_found_continue:
|
|
||||||
fprintf(stderr, "pahole: type (%s) not found\n", prototype->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct type *type = tag__type(class);
|
|
||||||
|
|
||||||
if (prototype->size) {
|
|
||||||
type->sizeof_member = type__find_member_by_name(type, cu, prototype->size);
|
|
||||||
if (type->sizeof_member == NULL) {
|
|
||||||
fprintf(stderr, "pahole: the sizeof member '%s' wasn't found in the '%s' type\n",
|
|
||||||
prototype->size, prototype->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prototype->type) {
|
|
||||||
type->type_member = type__find_member_by_name(type, cu, prototype->type);
|
|
||||||
if (type->type_member == NULL) {
|
|
||||||
fprintf(stderr, "pahole: the type member '%s' wasn't found in the '%s' type\n",
|
|
||||||
prototype->type, prototype->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prototype->type_enum) {
|
|
||||||
if (cus__find_type_enum(cus, type, prototype->type_enum)) {
|
|
||||||
fprintf(stderr, "pahole: type_enum=%s type not found for %s\n", prototype->type_enum, prototype->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prototype->filter) {
|
|
||||||
type->filter = class_member_filter__new(type, cu, prototype->filter);
|
|
||||||
if (type->filter == NULL) {
|
|
||||||
fprintf(stderr, "pahole: invalid filter '%s' for '%s'\n", prototype->filter, prototype->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isatty(0)) {
|
|
||||||
/*
|
|
||||||
* For the pretty printer only the first class is considered,
|
|
||||||
* ignore the rest.
|
|
||||||
*/
|
|
||||||
tag__stdio_fprintf_value(class, prototype, cu, cus, header, stdout);
|
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
struct type *type = tag__type(prototype->class);
|
||||||
|
|
||||||
|
if (prototype->type && !type->type_member) {
|
||||||
|
fprintf(stderr, "pahole: member 'type=%s' not found in '%s' type\n",
|
||||||
|
prototype->type, prototype->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prototype->size && !type->sizeof_member) {
|
||||||
|
fprintf(stderr, "pahole: member 'sizeof=%s' not found in '%s' type\n",
|
||||||
|
prototype->size, prototype->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prototype->filter && !type->filter) {
|
||||||
|
fprintf(stderr, "pahole: filter 'filter=%s' couldn't be evaluated for '%s' type\n",
|
||||||
|
prototype->filter, prototype->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prototype->type_enum && !prototype->type_enum_resolved) {
|
||||||
|
fprintf(stderr, "pahole: 'type_enum=%s' couldn't be evaluated for '%s' type\n",
|
||||||
|
prototype->type_enum, prototype->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue