2006-10-28 23:22:42 +02:00
|
|
|
/*
|
|
|
|
Copyright (C) 2006 Mandriva Conectiva S.A.
|
|
|
|
Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of version 2 of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <dwarf.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <libdw.h>
|
|
|
|
#include <libelf.h>
|
2006-11-04 21:37:23 +01:00
|
|
|
#include <search.h>
|
2006-10-28 23:22:42 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "list.h"
|
|
|
|
#include "classes.h"
|
|
|
|
|
2006-11-05 18:34:54 +01:00
|
|
|
unsigned int cacheline_size = DEFAULT_CACHELINE_SIZE;
|
|
|
|
|
2006-10-28 23:22:42 +02:00
|
|
|
static void *zalloc(const size_t size)
|
|
|
|
{
|
|
|
|
void *s = malloc(size);
|
|
|
|
if (s != NULL)
|
|
|
|
memset(s, 0, size);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2006-11-04 21:37:23 +01:00
|
|
|
static void *strings;
|
|
|
|
|
|
|
|
static int strings__compare(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
return strcmp(a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *strings__add(const char *str)
|
|
|
|
{
|
|
|
|
char **s;
|
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s = tsearch(str, &strings, strings__compare);
|
|
|
|
if (s != NULL) {
|
|
|
|
if (*s == str) {
|
|
|
|
char *dup = strdup(str);
|
|
|
|
if (dup != NULL)
|
|
|
|
*s = dup;
|
|
|
|
else {
|
|
|
|
tdelete(str, &strings, strings__compare);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return *s;
|
|
|
|
}
|
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
static struct variable *variable__new(const char *name, uint64_t id,
|
|
|
|
uint64_t type, uint64_t abstract_origin)
|
|
|
|
{
|
|
|
|
struct variable *self = malloc(sizeof(*self));
|
|
|
|
|
|
|
|
if (self != NULL) {
|
|
|
|
self->name = strings__add(name);
|
|
|
|
self->id = id;
|
|
|
|
self->type = type;
|
|
|
|
self->abstract_origin = abstract_origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
static void cus__add(struct cus *self, struct cu *cu)
|
2006-10-31 20:12:42 +01:00
|
|
|
{
|
2006-11-11 19:31:04 +01:00
|
|
|
list_add_tail(&cu->node, &self->cus);
|
2006-10-31 20:12:42 +01:00
|
|
|
}
|
|
|
|
|
2006-11-03 18:38:43 +01:00
|
|
|
static struct cu *cu__new(unsigned int cu, const char *name)
|
2006-10-31 20:12:42 +01:00
|
|
|
{
|
|
|
|
struct cu *self = malloc(sizeof(*self));
|
|
|
|
|
2006-11-03 18:38:43 +01:00
|
|
|
if (self != NULL) {
|
2006-10-31 20:12:42 +01:00
|
|
|
INIT_LIST_HEAD(&self->classes);
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
INIT_LIST_HEAD(&self->variables);
|
2006-11-04 21:37:23 +01:00
|
|
|
self->name = strings__add(name);
|
2006-11-03 18:38:43 +01:00
|
|
|
self->nr_inline_expansions = 0;
|
|
|
|
self->size_inline_expansions = 0;
|
2006-11-12 15:29:33 +01:00
|
|
|
self->nr_structures_changed = 0;
|
2006-11-11 19:31:04 +01:00
|
|
|
self->nr_functions_changed = 0;
|
2006-11-12 15:29:33 +01:00
|
|
|
self->max_len_changed_item = 0;
|
2006-11-11 19:31:04 +01:00
|
|
|
self->function_bytes_added = 0;
|
|
|
|
self->function_bytes_removed = 0;
|
2006-11-03 18:38:43 +01:00
|
|
|
}
|
2006-10-31 20:12:42 +01:00
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
static void cu__add_class(struct cu *self, struct class *class)
|
2006-10-31 20:12:42 +01:00
|
|
|
{
|
2006-11-12 19:59:47 +01:00
|
|
|
class->cu = self;
|
2006-10-31 20:12:42 +01:00
|
|
|
list_add_tail(&class->node, &self->classes);
|
|
|
|
}
|
2006-10-28 23:22:42 +02:00
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
static void cu__add_variable(struct cu *self, struct variable *variable)
|
|
|
|
{
|
|
|
|
list_add_tail(&variable->cu_node, &self->variables);
|
|
|
|
}
|
|
|
|
|
2006-10-28 23:22:42 +02:00
|
|
|
static const char *tag_name(const unsigned int tag)
|
|
|
|
{
|
|
|
|
switch (tag) {
|
|
|
|
case DW_TAG_enumeration_type: return "enum ";
|
|
|
|
case DW_TAG_structure_type: return "struct ";
|
|
|
|
case DW_TAG_union_type: return "union ";
|
|
|
|
case DW_TAG_pointer_type: return " *";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *cu__find_class_by_name(struct cu *self, const char *name)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
struct class *pos;
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
if (name == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
list_for_each_entry(pos, &self->classes, node)
|
2006-11-02 17:48:35 +01:00
|
|
|
if (pos->name != NULL && strcmp(pos->name, name) == 0)
|
2006-10-28 23:22:42 +02:00
|
|
|
return pos;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
struct class *cus__find_class_by_name(struct cus *self, struct cu **cu,
|
|
|
|
const char *name)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
2006-10-31 21:23:16 +01:00
|
|
|
struct cu *cu_pos;
|
2006-10-28 23:22:42 +02:00
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
list_for_each_entry(cu_pos, &self->cus, node) {
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *class = cu__find_class_by_name(cu_pos, name);
|
|
|
|
|
|
|
|
if (class != NULL) {
|
|
|
|
*cu = cu_pos;
|
|
|
|
return class;
|
|
|
|
}
|
|
|
|
}
|
2006-10-31 20:12:42 +01:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
struct cu *cus__find_cu_by_name(struct cus *self, const char *name)
|
|
|
|
{
|
|
|
|
struct cu *pos;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, &self->cus, node)
|
|
|
|
if (strcmp(pos->name, name) == 0)
|
|
|
|
return pos;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-11-05 03:46:22 +01:00
|
|
|
struct class *cu__find_class_by_id(const struct cu *self, const uint64_t id)
|
2006-10-31 20:12:42 +01:00
|
|
|
{
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *pos;
|
2006-10-31 20:12:42 +01:00
|
|
|
|
2006-10-31 21:23:16 +01:00
|
|
|
list_for_each_entry(pos, &self->classes, node)
|
2006-10-31 20:12:42 +01:00
|
|
|
if (pos->id == id)
|
2006-10-28 23:22:42 +02:00
|
|
|
return pos;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
struct variable *cu__find_variable_by_id(const struct cu *self, const uint64_t id)
|
|
|
|
{
|
|
|
|
struct variable *pos;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, &self->variables, cu_node)
|
|
|
|
if (pos->id == id)
|
|
|
|
return pos;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-11-05 05:17:19 +01:00
|
|
|
int class__is_struct(const struct class *self, struct cu *cu,
|
|
|
|
struct class **typedef_alias)
|
|
|
|
{
|
|
|
|
*typedef_alias = NULL;
|
|
|
|
if (self->tag == DW_TAG_typedef) {
|
|
|
|
*typedef_alias = cu__find_class_by_id(cu, self->type);
|
|
|
|
if (*typedef_alias == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return (*typedef_alias)->tag == DW_TAG_structure_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self->tag == DW_TAG_structure_type;
|
|
|
|
}
|
|
|
|
|
2006-11-11 17:15:50 +01:00
|
|
|
static uint64_t class__size(const struct class *self,
|
|
|
|
const struct cu *cu)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t size = self->size;
|
2006-10-28 23:22:42 +02:00
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
if (self->tag != DW_TAG_pointer_type && self->type != 0) {
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *class = cu__find_class_by_id(cu, self->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (class != NULL)
|
2006-10-31 20:12:42 +01:00
|
|
|
size = class__size(class, cu);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (self->tag == DW_TAG_array_type)
|
|
|
|
size *= self->nr_entries;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
static const char *class__name(struct class *self, const struct cu *cu,
|
|
|
|
char *bf, size_t len)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
if (self->tag == DW_TAG_pointer_type) {
|
2006-10-31 20:12:42 +01:00
|
|
|
if (self->type == 0) /* No type == void */
|
2006-10-28 23:22:42 +02:00
|
|
|
strncpy(bf, "void *", len);
|
|
|
|
else {
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *ptr_class =
|
|
|
|
cu__find_class_by_id(cu, self->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (ptr_class != NULL) {
|
|
|
|
char ptr_class_name[128];
|
|
|
|
snprintf(bf, len, "%s *",
|
2006-10-31 20:12:42 +01:00
|
|
|
class__name(ptr_class, cu,
|
2006-10-28 23:22:42 +02:00
|
|
|
ptr_class_name,
|
|
|
|
sizeof(ptr_class_name)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (self->tag == DW_TAG_volatile_type ||
|
|
|
|
self->tag == DW_TAG_const_type) {
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *vol_class = cu__find_class_by_id(cu, self->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (vol_class != NULL) {
|
|
|
|
char vol_class_name[128];
|
|
|
|
snprintf(bf, len, "%s %s ",
|
|
|
|
self->tag == DW_TAG_volatile_type ?
|
|
|
|
"volatile" : "const",
|
2006-10-31 20:12:42 +01:00
|
|
|
class__name(vol_class, cu,
|
2006-10-28 23:22:42 +02:00
|
|
|
vol_class_name,
|
|
|
|
sizeof(vol_class_name)));
|
|
|
|
}
|
|
|
|
} else if (self->tag == DW_TAG_array_type) {
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *ptr_class = cu__find_class_by_id(cu, self->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (ptr_class != NULL)
|
2006-10-31 20:12:42 +01:00
|
|
|
return class__name(ptr_class, cu, bf, len);
|
2006-10-28 23:22:42 +02:00
|
|
|
} else
|
2006-11-02 17:48:35 +01:00
|
|
|
snprintf(bf, len, "%s%s", tag_name(self->tag),
|
|
|
|
self->name ?: "");
|
2006-10-28 23:22:42 +02:00
|
|
|
return bf;
|
|
|
|
}
|
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
static const char *variable__type_name(struct variable *self, const struct cu *cu,
|
|
|
|
char *bf, size_t len)
|
|
|
|
{
|
|
|
|
if (self->type != 0) {
|
|
|
|
struct class *class = cu__find_class_by_id(cu, self->type);
|
|
|
|
if (class == NULL)
|
|
|
|
return NULL;
|
|
|
|
return class__name(class, cu, bf, len);
|
|
|
|
} else if (self->abstract_origin != 0) {
|
|
|
|
struct variable *var;
|
|
|
|
|
|
|
|
var = cu__find_variable_by_id(cu, self->abstract_origin);
|
|
|
|
if (var != NULL)
|
|
|
|
return variable__type_name(var, cu, bf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *variable__name(struct variable *self, const struct cu *cu)
|
|
|
|
{
|
|
|
|
if (self->name == NULL) {
|
|
|
|
if (self->abstract_origin == 0)
|
|
|
|
return NULL;
|
|
|
|
else {
|
|
|
|
struct variable *var;
|
|
|
|
|
|
|
|
var = cu__find_variable_by_id(cu, self->abstract_origin);
|
|
|
|
return var == NULL ? NULL : var->name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return self->name;
|
|
|
|
}
|
|
|
|
|
2006-11-05 03:46:22 +01:00
|
|
|
static struct class_member *class_member__new(uint64_t type,
|
2006-10-28 23:22:42 +02:00
|
|
|
const char *name,
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t offset,
|
2006-10-28 23:22:42 +02:00
|
|
|
unsigned int bit_size,
|
|
|
|
unsigned int bit_offset)
|
|
|
|
{
|
|
|
|
struct class_member *self = zalloc(sizeof(*self));
|
|
|
|
|
|
|
|
if (self != NULL) {
|
2006-10-31 20:12:42 +01:00
|
|
|
self->type = type;
|
2006-10-28 23:22:42 +02:00
|
|
|
self->offset = offset;
|
|
|
|
self->bit_size = bit_size;
|
|
|
|
self->bit_offset = bit_offset;
|
2006-11-04 21:37:23 +01:00
|
|
|
self->name = strings__add(name);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
static int class_member__size(const struct class_member *self,
|
|
|
|
const struct cu *cu)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *class = cu__find_class_by_id(cu, self->type);
|
2006-10-31 20:12:42 +01:00
|
|
|
return class != NULL ? class__size(class, cu) : -1;
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
uint64_t class_member__names(const struct class_member *self,
|
|
|
|
const struct cu *cu,
|
|
|
|
char *class_name, size_t class_name_size,
|
|
|
|
char *member_name, size_t member_name_size)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *class = cu__find_class_by_id(cu, self->type);
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t size = -1;
|
2006-10-28 23:22:42 +02:00
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
snprintf(member_name, member_name_size, "%s;", self->name ?: "");
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (class == NULL)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
snprintf(class_name, class_name_size, "<%llx>",
|
|
|
|
self->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
else {
|
2006-10-31 20:12:42 +01:00
|
|
|
size = class__size(class, cu);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
/* Is it a function pointer? */
|
|
|
|
if (class->tag == DW_TAG_pointer_type) {
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *ptr_class =
|
|
|
|
cu__find_class_by_id(cu, class->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (ptr_class != NULL &&
|
|
|
|
ptr_class->tag == DW_TAG_subroutine_type) {
|
|
|
|
/* function has no return value (void) */
|
2006-10-31 20:12:42 +01:00
|
|
|
if (ptr_class->type == 0)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
snprintf(class_name,
|
|
|
|
class_name_size, "void");
|
2006-10-28 23:22:42 +02:00
|
|
|
else {
|
|
|
|
struct class *ret_class =
|
2006-10-31 21:23:16 +01:00
|
|
|
cu__find_class_by_id(cu,
|
|
|
|
ptr_class->type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (ret_class != NULL)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
class__name(ret_class, cu,
|
|
|
|
class_name,
|
|
|
|
class_name_size);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
snprintf(member_name, member_name_size,
|
2006-11-02 17:48:35 +01:00
|
|
|
"(*%s)();", self->name ?: "");
|
2006-10-28 23:22:42 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
class__name(class, cu, class_name, class_name_size);
|
2006-10-28 23:22:42 +02:00
|
|
|
if (class->tag == DW_TAG_array_type)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
snprintf(member_name, member_name_size,
|
2006-11-11 17:15:50 +01:00
|
|
|
"%s[%llu];", self->name ?: "",
|
2006-11-02 17:48:35 +01:00
|
|
|
class->nr_entries);
|
2006-10-28 23:22:42 +02:00
|
|
|
else if (self->bit_size != 0)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
snprintf(member_name, member_name_size,
|
2006-11-02 17:48:35 +01:00
|
|
|
"%s:%d;", self->name ?: "",
|
|
|
|
self->bit_size);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
out:
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t class_member__print(struct class_member *self,
|
|
|
|
const struct cu *cu)
|
|
|
|
{
|
|
|
|
uint64_t size;
|
|
|
|
char class_name[128];
|
|
|
|
char member_name[128];
|
|
|
|
|
|
|
|
size = class_member__names(self, cu,
|
|
|
|
class_name, sizeof(class_name),
|
|
|
|
member_name, sizeof(member_name));
|
|
|
|
|
2006-11-11 00:09:47 +01:00
|
|
|
printf(" %-26s %-21s /* %5llu %5llu */\n",
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
class_name, member_name, self->offset, size);
|
2006-10-28 23:22:42 +02:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2006-11-05 03:46:22 +01:00
|
|
|
static struct inline_expansion *inline_expansion__new(uint64_t type,
|
2006-11-03 18:32:32 +01:00
|
|
|
uint64_t size)
|
2006-11-03 16:41:19 +01:00
|
|
|
{
|
|
|
|
struct inline_expansion *self = zalloc(sizeof(*self));
|
|
|
|
|
|
|
|
if (self != NULL) {
|
|
|
|
self->type = type;
|
|
|
|
self->size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
static struct class *class__new(const unsigned int tag,
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t cu_offset, uint64_t type,
|
|
|
|
const char *name, uint64_t size,
|
2006-10-29 02:40:35 +02:00
|
|
|
const char *decl_file, unsigned int decl_line,
|
2006-10-30 18:22:39 +01:00
|
|
|
unsigned short inlined,
|
2006-11-03 18:32:32 +01:00
|
|
|
uint64_t low_pc, uint64_t high_pc)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
struct class *self = malloc(sizeof(*self));
|
|
|
|
|
|
|
|
if (self != NULL) {
|
|
|
|
INIT_LIST_HEAD(&self->members);
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
INIT_LIST_HEAD(&self->variables);
|
2006-11-03 16:41:19 +01:00
|
|
|
INIT_LIST_HEAD(&self->inline_expansions);
|
2006-10-28 23:22:42 +02:00
|
|
|
self->tag = tag;
|
2006-11-12 19:59:47 +01:00
|
|
|
self->cu = NULL;
|
2006-10-31 20:12:42 +01:00
|
|
|
self->id = cu_offset;
|
|
|
|
self->type = type;
|
2006-10-28 23:22:42 +02:00
|
|
|
self->size = size;
|
2006-11-04 21:37:23 +01:00
|
|
|
self->name = strings__add(name);
|
|
|
|
self->decl_file = strings__add(decl_file);
|
2006-10-28 23:22:42 +02:00
|
|
|
self->decl_line = decl_line;
|
|
|
|
self->nr_holes = 0;
|
2006-11-01 14:18:01 +01:00
|
|
|
self->nr_labels = 0;
|
2006-11-01 14:34:42 +01:00
|
|
|
self->nr_members = 0;
|
2006-11-01 14:18:01 +01:00
|
|
|
self->nr_variables = 0;
|
2006-11-10 22:19:58 +01:00
|
|
|
self->refcnt = 0;
|
2006-10-28 23:22:42 +02:00
|
|
|
self->padding = 0;
|
2006-11-03 16:41:19 +01:00
|
|
|
self->nr_inline_expansions = 0;
|
|
|
|
self->size_inline_expansions = 0;
|
2006-10-29 02:40:35 +02:00
|
|
|
self->inlined = inlined;
|
2006-10-30 18:22:39 +01:00
|
|
|
self->low_pc = low_pc;
|
|
|
|
self->high_pc = high_pc;
|
[PFUNCT]: Improve --cu_inline_expansions_stats
Now it shows the number that each of the inline functions were expanded in an
object file:
Top 10 inline functions expanded more than once in kernel/sched.o, by total
size of inline expansions:
[acme@newtoy guinea_pig-2.6]$ pfunct --cu_inline_expansions_stats kernel/sched.o | sort -k3 -nr | grep -v ': 1 ' | head -11
kernel/sched.c: 318 10217
get_current: 38 325
finish_task_switch: 2 238
normal_prio: 2 167
__cpus_and: 14 164
find_process_by_pid: 6 152
current_thread_info: 21 149
sched_find_first_bit: 2 148
update_cpu_clock: 2 140
task_rq_unlock: 14 137
variable_test_bit: 14 121
[acme@newtoy guinea_pig-2.6]$
Now we have these options:
[acme@newtoy guinea_pig-2.6]$ pfunct --help
usage: pfunct [options] <file_name> {<function_name>}
where:
-c, --class=<class> functions that have <class> pointer parameters
-g, --goto_labels show number of goto labels
-i, --show_inline_expansions show inline expansions
-C, --cu_inline_expansions_stats show CU inline expansions stats
-s, --sizes show size of functions
-N, --function_name_len show size of functions
-p, --nr_parameters show number or parameters
-S, --variables show number of variables
-V, --verbose be verbose
[acme@newtoy guinea_pig-2.6]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-03 19:22:12 +01:00
|
|
|
self->cu_total_nr_inline_expansions = 0;
|
|
|
|
self->cu_total_size_inline_expansions = 0;
|
2006-11-11 19:31:04 +01:00
|
|
|
self->diff = 0;
|
2006-11-12 15:56:52 +01:00
|
|
|
self->class_to_diff = NULL;
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void class__add_member(struct class *self, struct class_member *member)
|
|
|
|
{
|
2006-11-01 14:34:42 +01:00
|
|
|
++self->nr_members;
|
2006-10-28 23:22:42 +02:00
|
|
|
list_add_tail(&member->node, &self->members);
|
|
|
|
}
|
|
|
|
|
2006-11-03 16:41:19 +01:00
|
|
|
static void class__add_inline_expansion(struct class *self,
|
|
|
|
struct inline_expansion *exp)
|
|
|
|
{
|
|
|
|
++self->nr_inline_expansions;
|
|
|
|
self->size_inline_expansions += exp->size;
|
|
|
|
list_add_tail(&exp->node, &self->inline_expansions);
|
|
|
|
}
|
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
static void class__add_variable(struct class *self, struct variable *var)
|
|
|
|
{
|
|
|
|
++self->nr_variables;
|
|
|
|
list_add_tail(&var->class_node, &self->variables);
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
void class__find_holes(struct class *self, const struct cu *cu)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
struct class_member *pos, *last = NULL;
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t last_size = 0, size;
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
self->nr_holes = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, &self->members, node) {
|
|
|
|
if (last != NULL) {
|
|
|
|
const int cc_last_size = pos->offset - last->offset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the offset is the same this better
|
|
|
|
* be a bitfield or an empty struct (see
|
|
|
|
* rwlock_t in the Linux kernel sources when
|
|
|
|
* compiled for UP) or...
|
|
|
|
*/
|
|
|
|
if (cc_last_size > 0) {
|
|
|
|
last->hole = cc_last_size - last_size;
|
|
|
|
if (last->hole > 0)
|
|
|
|
++self->nr_holes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
size = class_member__size(pos, cu);
|
2006-10-28 23:22:42 +02:00
|
|
|
/*
|
|
|
|
* check for bitfields, accounting for only the biggest
|
|
|
|
* of the byte_size in the fields in each bitfield set.
|
|
|
|
*/
|
|
|
|
if (last == NULL || last->offset != pos->offset ||
|
|
|
|
pos->bit_size == 0 || last->bit_size == 0) {
|
|
|
|
last_size = size;
|
|
|
|
} else if (size > last_size)
|
|
|
|
last_size = size;
|
|
|
|
|
|
|
|
last = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last != NULL && last->offset + last_size != self->size)
|
|
|
|
self->padding = self->size - (last->offset + last_size);
|
|
|
|
}
|
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
struct class_member *class__find_member_by_name(const struct class *self,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct class_member *pos;
|
|
|
|
|
2006-11-12 18:43:28 +01:00
|
|
|
if (name == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
list_for_each_entry(pos, &self->members, node)
|
|
|
|
if (pos->name != NULL && strcmp(pos->name, name) == 0)
|
|
|
|
return pos;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
[PFUNCT]: Improve --cu_inline_expansions_stats
Now it shows the number that each of the inline functions were expanded in an
object file:
Top 10 inline functions expanded more than once in kernel/sched.o, by total
size of inline expansions:
[acme@newtoy guinea_pig-2.6]$ pfunct --cu_inline_expansions_stats kernel/sched.o | sort -k3 -nr | grep -v ': 1 ' | head -11
kernel/sched.c: 318 10217
get_current: 38 325
finish_task_switch: 2 238
normal_prio: 2 167
__cpus_and: 14 164
find_process_by_pid: 6 152
current_thread_info: 21 149
sched_find_first_bit: 2 148
update_cpu_clock: 2 140
task_rq_unlock: 14 137
variable_test_bit: 14 121
[acme@newtoy guinea_pig-2.6]$
Now we have these options:
[acme@newtoy guinea_pig-2.6]$ pfunct --help
usage: pfunct [options] <file_name> {<function_name>}
where:
-c, --class=<class> functions that have <class> pointer parameters
-g, --goto_labels show number of goto labels
-i, --show_inline_expansions show inline expansions
-C, --cu_inline_expansions_stats show CU inline expansions stats
-s, --sizes show size of functions
-N, --function_name_len show size of functions
-p, --nr_parameters show number or parameters
-S, --variables show number of variables
-V, --verbose be verbose
[acme@newtoy guinea_pig-2.6]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-03 19:22:12 +01:00
|
|
|
void class__account_inline_expansions(struct class *self, struct cu *cu)
|
|
|
|
{
|
|
|
|
struct class *class_type;
|
|
|
|
struct inline_expansion *pos;
|
|
|
|
|
|
|
|
if (self->nr_inline_expansions == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, &self->inline_expansions, node) {
|
|
|
|
class_type = cu__find_class_by_id(cu, pos->type);
|
|
|
|
if (class_type != NULL) {
|
|
|
|
class_type->cu_total_nr_inline_expansions++;
|
|
|
|
class_type->cu_total_size_inline_expansions += pos->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2006-11-03 18:38:43 +01:00
|
|
|
|
|
|
|
void cu__account_inline_expansions(struct cu *self)
|
|
|
|
{
|
|
|
|
struct class *pos;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, &self->classes, node) {
|
[PFUNCT]: Improve --cu_inline_expansions_stats
Now it shows the number that each of the inline functions were expanded in an
object file:
Top 10 inline functions expanded more than once in kernel/sched.o, by total
size of inline expansions:
[acme@newtoy guinea_pig-2.6]$ pfunct --cu_inline_expansions_stats kernel/sched.o | sort -k3 -nr | grep -v ': 1 ' | head -11
kernel/sched.c: 318 10217
get_current: 38 325
finish_task_switch: 2 238
normal_prio: 2 167
__cpus_and: 14 164
find_process_by_pid: 6 152
current_thread_info: 21 149
sched_find_first_bit: 2 148
update_cpu_clock: 2 140
task_rq_unlock: 14 137
variable_test_bit: 14 121
[acme@newtoy guinea_pig-2.6]$
Now we have these options:
[acme@newtoy guinea_pig-2.6]$ pfunct --help
usage: pfunct [options] <file_name> {<function_name>}
where:
-c, --class=<class> functions that have <class> pointer parameters
-g, --goto_labels show number of goto labels
-i, --show_inline_expansions show inline expansions
-C, --cu_inline_expansions_stats show CU inline expansions stats
-s, --sizes show size of functions
-N, --function_name_len show size of functions
-p, --nr_parameters show number or parameters
-S, --variables show number of variables
-V, --verbose be verbose
[acme@newtoy guinea_pig-2.6]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-03 19:22:12 +01:00
|
|
|
class__account_inline_expansions(pos, self);
|
2006-11-03 18:38:43 +01:00
|
|
|
self->nr_inline_expansions += pos->nr_inline_expansions;
|
|
|
|
self->size_inline_expansions += pos->size_inline_expansions;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-03 16:41:19 +01:00
|
|
|
void class__print_inline_expansions(struct class *self, const struct cu *cu)
|
|
|
|
{
|
|
|
|
char bf[256];
|
|
|
|
struct class *class_type;
|
|
|
|
const char *type = "<ERROR>";
|
|
|
|
struct inline_expansion *pos;
|
|
|
|
|
|
|
|
if (self->nr_inline_expansions == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
printf("/* inline expansions in %s:\n", self->name);
|
|
|
|
list_for_each_entry(pos, &self->inline_expansions, node) {
|
|
|
|
type = "<ERROR>";
|
|
|
|
class_type = cu__find_class_by_id(cu, pos->type);
|
|
|
|
if (class_type != NULL)
|
|
|
|
type = class__name(class_type, cu, bf, sizeof(bf));
|
2006-11-05 03:46:22 +01:00
|
|
|
printf("%s: %llu\n", type, pos->size);
|
2006-11-03 16:41:19 +01:00
|
|
|
}
|
|
|
|
fputs("*/\n", stdout);
|
|
|
|
}
|
|
|
|
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
void class__print_variables(struct class *self, const struct cu *cu)
|
|
|
|
{
|
|
|
|
struct variable *pos;
|
|
|
|
|
|
|
|
if (self->nr_variables == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
printf("{\n /* variables in %s: */\n", self->name);
|
|
|
|
list_for_each_entry(pos, &self->variables, class_node) {
|
|
|
|
char bf[256];
|
|
|
|
printf(" %s %s;\n",
|
|
|
|
variable__type_name(pos, cu, bf, sizeof(bf)),
|
|
|
|
variable__name(pos, cu));
|
|
|
|
}
|
|
|
|
fputs("}\n", stdout);
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
static void class__print_function(struct class *self, const struct cu *cu)
|
2006-10-28 23:49:27 +02:00
|
|
|
{
|
2006-10-29 00:04:40 +02:00
|
|
|
char bf[256];
|
|
|
|
struct class *class_type;
|
|
|
|
const char *type = "<ERROR>";
|
|
|
|
struct class_member *pos;
|
|
|
|
int first_parameter = 1;
|
2006-10-28 23:49:27 +02:00
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
if (self->type == 0)
|
2006-10-29 00:04:40 +02:00
|
|
|
type = "void";
|
2006-10-28 23:49:27 +02:00
|
|
|
else {
|
2006-10-31 21:23:16 +01:00
|
|
|
class_type = cu__find_class_by_id(cu, self->type);
|
2006-10-29 00:04:40 +02:00
|
|
|
if (class_type != NULL)
|
2006-10-31 20:12:42 +01:00
|
|
|
type = class__name(class_type, cu, bf, sizeof(bf));
|
2006-10-29 00:04:40 +02:00
|
|
|
}
|
2006-10-28 23:49:27 +02:00
|
|
|
|
2006-11-02 17:48:35 +01:00
|
|
|
printf("%s%s %s(", self->inlined ? "inline " : "",
|
|
|
|
type, self->name ?: "");
|
2006-10-29 00:04:40 +02:00
|
|
|
list_for_each_entry(pos, &self->members, node) {
|
|
|
|
if (!first_parameter)
|
|
|
|
fputs(", ", stdout);
|
|
|
|
else
|
|
|
|
first_parameter = 0;
|
|
|
|
type = "<ERROR>";
|
2006-10-31 21:23:16 +01:00
|
|
|
class_type = cu__find_class_by_id(cu, pos->type);
|
2006-10-29 00:04:40 +02:00
|
|
|
if (class_type != NULL)
|
2006-10-31 20:12:42 +01:00
|
|
|
type = class__name(class_type, cu, bf, sizeof(bf));
|
2006-11-02 17:48:35 +01:00
|
|
|
printf("%s %s", type, pos->name ?: "");
|
2006-10-28 23:49:27 +02:00
|
|
|
}
|
|
|
|
|
2006-10-29 00:04:40 +02:00
|
|
|
/* No parameters? */
|
|
|
|
if (first_parameter)
|
|
|
|
fputs("void", stdout);
|
|
|
|
fputs(");\n", stdout);
|
2006-11-04 04:18:28 +01:00
|
|
|
if (self->size == 0)
|
|
|
|
return;
|
2006-11-03 18:32:32 +01:00
|
|
|
printf("/* size: %llu", self->high_pc - self->low_pc);
|
2006-11-01 14:18:01 +01:00
|
|
|
if (self->nr_variables > 0)
|
|
|
|
printf(", variables: %u", self->nr_variables);
|
|
|
|
if (self->nr_labels > 0)
|
|
|
|
printf(", goto labels: %u", self->nr_labels);
|
2006-11-03 16:41:19 +01:00
|
|
|
if (self->nr_inline_expansions > 0)
|
|
|
|
printf(", inline expansions: %u (%u bytes)",
|
|
|
|
self->nr_inline_expansions, self->size_inline_expansions);
|
2006-11-01 14:18:01 +01:00
|
|
|
fputs(" */\n", stdout);
|
2006-10-28 23:49:27 +02:00
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
static void class__print_struct(struct class *self, const struct cu *cu)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
unsigned long sum = 0;
|
|
|
|
unsigned long sum_holes = 0;
|
|
|
|
struct class_member *pos;
|
|
|
|
char name[128];
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t last_size = 0, size;
|
2006-10-28 23:22:42 +02:00
|
|
|
int last_bit_size = 0;
|
|
|
|
int last_offset = -1;
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
printf("%s {\n", class__name(self, cu, name, sizeof(name)));
|
2006-10-28 23:22:42 +02:00
|
|
|
list_for_each_entry(pos, &self->members, node) {
|
2006-11-05 21:30:10 +01:00
|
|
|
if (sum > 0 && last_size > 0 && sum % cacheline_size == 0)
|
2006-11-05 18:34:54 +01:00
|
|
|
printf(" /* ---------- cacheline "
|
2006-11-11 17:15:50 +01:00
|
|
|
"%lu boundary ---------- */\n",
|
2006-11-05 18:34:54 +01:00
|
|
|
sum / cacheline_size);
|
2006-10-31 20:12:42 +01:00
|
|
|
size = class_member__print(pos, cu);
|
2006-10-28 23:22:42 +02:00
|
|
|
if (pos->hole > 0) {
|
|
|
|
printf("\n /* XXX %d bytes hole, "
|
|
|
|
"try to pack */\n\n", pos->hole);
|
|
|
|
sum_holes += pos->hole;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* check for bitfields, accounting for only the biggest
|
|
|
|
* of the byte_size in the fields in each bitfield set.
|
|
|
|
*/
|
|
|
|
if (last_offset != pos->offset ||
|
|
|
|
pos->bit_size == 0 || last_bit_size == 0) {
|
|
|
|
last_size = size;
|
|
|
|
sum += last_size;
|
|
|
|
} else if (size > last_size) {
|
|
|
|
sum += size - last_size;
|
|
|
|
last_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
last_offset = pos->offset;
|
|
|
|
last_bit_size = pos->bit_size;
|
|
|
|
}
|
|
|
|
|
2006-11-05 03:46:22 +01:00
|
|
|
printf("}; /* size: %llu", self->size);
|
2006-10-28 23:22:42 +02:00
|
|
|
if (sum_holes > 0)
|
|
|
|
printf(", sum members: %lu, holes: %d, sum holes: %lu",
|
|
|
|
sum, self->nr_holes, sum_holes);
|
|
|
|
if (self->padding > 0)
|
|
|
|
printf(", padding: %u", self->padding);
|
|
|
|
puts(" */");
|
|
|
|
|
|
|
|
if (sum + sum_holes != self->size - self->padding)
|
2006-11-11 17:15:50 +01:00
|
|
|
printf("\n/* BRAIN FART ALERT! %llu != "
|
|
|
|
"%lu + %lu(holes), diff = %llu */\n\n",
|
2006-10-28 23:22:42 +02:00
|
|
|
self->size, sum, sum_holes,
|
|
|
|
self->size - (sum + sum_holes));
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
void class__print(struct class *self, const struct cu *cu)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
2006-10-30 19:24:32 +01:00
|
|
|
printf("/* %s:%u */\n", self->decl_file, self->decl_line);
|
2006-10-29 00:13:27 +02:00
|
|
|
|
2006-10-28 23:22:42 +02:00
|
|
|
switch (self->tag) {
|
|
|
|
case DW_TAG_structure_type:
|
2006-10-31 20:12:42 +01:00
|
|
|
class__print_struct(self, cu);
|
2006-10-28 23:22:42 +02:00
|
|
|
break;
|
2006-10-28 23:49:27 +02:00
|
|
|
case DW_TAG_subprogram:
|
2006-10-31 20:12:42 +01:00
|
|
|
class__print_function(self, cu);
|
2006-10-28 23:49:27 +02:00
|
|
|
break;
|
2006-10-28 23:22:42 +02:00
|
|
|
default:
|
2006-11-02 17:48:35 +01:00
|
|
|
printf("%s%s;\n", tag_name(self->tag), self->name ?: "");
|
2006-10-28 23:22:42 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-10-29 00:13:27 +02:00
|
|
|
putchar('\n');
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
2006-10-31 21:23:16 +01:00
|
|
|
int cu__for_each_class(struct cu *cu,
|
|
|
|
int (*iterator)(struct cu *cu,
|
|
|
|
struct class *class,
|
|
|
|
void *cookie),
|
|
|
|
void *cookie)
|
2006-10-29 03:55:56 +01:00
|
|
|
{
|
|
|
|
|
2006-10-31 21:23:16 +01:00
|
|
|
struct class *pos;
|
2006-10-31 20:12:42 +01:00
|
|
|
|
2006-10-31 21:23:16 +01:00
|
|
|
list_for_each_entry(pos, &cu->classes, node)
|
|
|
|
if (iterator(cu, pos, cookie))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
void cus__for_each_cu(struct cus *self,
|
|
|
|
int (*iterator)(struct cu *cu, void *cookie),
|
2006-10-31 21:23:16 +01:00
|
|
|
void *cookie)
|
|
|
|
{
|
|
|
|
struct cu *pos;
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
list_for_each_entry(pos, &self->cus, node)
|
2006-10-31 21:23:16 +01:00
|
|
|
if (iterator(pos, cookie))
|
|
|
|
break;
|
2006-10-29 03:55:56 +01:00
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
void cus__print_classes(struct cus *self, const unsigned int tag)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
2006-10-31 20:12:42 +01:00
|
|
|
struct cu *cu_pos;
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
list_for_each_entry(cu_pos, &self->cus, node) {
|
2006-10-31 20:12:42 +01:00
|
|
|
struct class *class_pos;
|
2006-10-28 23:22:42 +02:00
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
list_for_each_entry(class_pos, &cu_pos->classes, node)
|
2006-11-02 17:48:35 +01:00
|
|
|
if (class_pos->tag == tag && class_pos->name != NULL) {
|
2006-11-05 21:32:20 +01:00
|
|
|
if (tag == DW_TAG_structure_type)
|
2006-10-31 20:12:42 +01:00
|
|
|
class__find_holes(class_pos, cu_pos);
|
|
|
|
class__print(class_pos, cu_pos);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
2006-10-31 20:12:42 +01:00
|
|
|
}
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
static struct class *cu__current_class;
|
2006-10-31 20:12:42 +01:00
|
|
|
static struct cu *current_cu;
|
|
|
|
static unsigned int current_cu_id;
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
static void oom(const char *msg)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "pahole: out of memory(%s)\n", msg);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *attr_string(Dwarf_Die *die, unsigned int name,
|
|
|
|
Dwarf_Attribute *attr)
|
|
|
|
{
|
|
|
|
if (dwarf_attr(die, name, attr) != NULL)
|
|
|
|
return dwarf_formstring(attr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Number decoding macros. See 7.6 Variable Length Data. */
|
|
|
|
|
|
|
|
#define get_uleb128_step(var, addr, nth, break) \
|
|
|
|
__b = *(addr)++; \
|
|
|
|
var |= (uintmax_t) (__b & 0x7f) << (nth * 7); \
|
|
|
|
if ((__b & 0x80) == 0) \
|
|
|
|
break
|
|
|
|
|
|
|
|
#define get_uleb128_rest_return(var, i, addrp) \
|
|
|
|
do { \
|
|
|
|
for (; i < 10; ++i) { \
|
|
|
|
get_uleb128_step(var, *addrp, i, \
|
|
|
|
return var); \
|
|
|
|
} \
|
|
|
|
/* Other implementations set VALUE to UINT_MAX in this \
|
|
|
|
case. So we better do this as well. */ \
|
|
|
|
return UINT64_MAX; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static uint64_t __libdw_get_uleb128(uint64_t acc, unsigned int i,
|
|
|
|
const unsigned char **addrp)
|
|
|
|
{
|
|
|
|
unsigned char __b;
|
|
|
|
get_uleb128_rest_return (acc, i, addrp);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define get_uleb128(var, addr) \
|
|
|
|
do { \
|
|
|
|
unsigned char __b; \
|
|
|
|
var = 0; \
|
|
|
|
get_uleb128_step(var, addr, 0, break); \
|
|
|
|
var = __libdw_get_uleb128 (var, 1, &(addr)); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
2006-11-05 03:46:22 +01:00
|
|
|
static uint64_t attr_offset(Dwarf_Die *die)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
Dwarf_Attribute attr;
|
|
|
|
|
|
|
|
if (dwarf_attr(die, DW_AT_data_member_location, &attr) != NULL) {
|
|
|
|
Dwarf_Block block;
|
|
|
|
|
|
|
|
if (dwarf_formblock(&attr, &block) == 0) {
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t uleb;
|
2006-10-28 23:22:42 +02:00
|
|
|
const unsigned char *data = block.data + 1;
|
|
|
|
get_uleb128(uleb, data);
|
|
|
|
return uleb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-11 17:15:50 +01:00
|
|
|
static uint64_t attr_upper_bound(Dwarf_Die *die)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
Dwarf_Attribute attr;
|
|
|
|
|
|
|
|
if (dwarf_attr(die, DW_AT_upper_bound, &attr) != NULL) {
|
|
|
|
Dwarf_Word num;
|
|
|
|
|
|
|
|
if (dwarf_formudata(&attr, &num) == 0) {
|
|
|
|
return (uintmax_t)num + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-05 03:46:22 +01:00
|
|
|
static uint64_t attr_numeric(Dwarf_Die *die, unsigned int name)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
Dwarf_Attribute attr;
|
|
|
|
unsigned int form;
|
|
|
|
|
|
|
|
if (dwarf_attr(die, name, &attr) == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
form = dwarf_whatform(&attr);
|
|
|
|
|
|
|
|
switch (form) {
|
2006-10-30 18:22:39 +01:00
|
|
|
case DW_FORM_addr: {
|
|
|
|
Dwarf_Addr addr;
|
|
|
|
if (dwarf_formaddr(&attr, &addr) == 0)
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
break;
|
2006-10-28 23:22:42 +02:00
|
|
|
case DW_FORM_data1:
|
|
|
|
case DW_FORM_data2:
|
|
|
|
case DW_FORM_data4:
|
|
|
|
case DW_FORM_data8:
|
|
|
|
case DW_FORM_sdata:
|
|
|
|
case DW_FORM_udata: {
|
|
|
|
Dwarf_Word value;
|
|
|
|
if (dwarf_formudata(&attr, &value) == 0)
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DW_FORM_ref1:
|
|
|
|
case DW_FORM_ref2:
|
|
|
|
case DW_FORM_ref4:
|
|
|
|
case DW_FORM_ref8:
|
|
|
|
case DW_FORM_ref_addr:
|
|
|
|
case DW_FORM_ref_udata: {
|
|
|
|
Dwarf_Off ref;
|
|
|
|
if (dwarf_formref(&attr, &ref) == 0)
|
|
|
|
return (uintmax_t)ref;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
printf("DW_AT_<0x%x>=0x%x\n", name, form);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
static void cu__process_die(Dwarf *dwarf, Dwarf_Die *die)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
Dwarf_Die child;
|
|
|
|
Dwarf_Off cu_offset;
|
|
|
|
Dwarf_Attribute attr_name;
|
2006-10-30 17:37:04 +01:00
|
|
|
const char *name;
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t type;
|
2006-10-28 23:22:42 +02:00
|
|
|
unsigned int tag = dwarf_tag(die);
|
|
|
|
|
|
|
|
if (tag == DW_TAG_invalid)
|
|
|
|
return;
|
|
|
|
|
2006-11-10 22:19:58 +01:00
|
|
|
/* Tags we trow away */
|
|
|
|
if (tag == DW_TAG_compile_unit)
|
|
|
|
goto children;
|
|
|
|
|
2006-10-30 17:37:04 +01:00
|
|
|
cu_offset = dwarf_cuoffset(die);
|
|
|
|
name = attr_string(die, DW_AT_name, &attr_name);
|
|
|
|
type = attr_numeric(die, DW_AT_type);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
2006-10-29 00:04:40 +02:00
|
|
|
if (tag == DW_TAG_member || tag == DW_TAG_formal_parameter) {
|
2006-10-28 23:22:42 +02:00
|
|
|
struct class_member *member;
|
|
|
|
|
2006-10-31 20:12:42 +01:00
|
|
|
member = class_member__new(type, name, attr_offset(die),
|
2006-10-30 17:37:04 +01:00
|
|
|
attr_numeric(die, DW_AT_bit_size),
|
|
|
|
attr_numeric(die, DW_AT_bit_offset));
|
2006-10-28 23:22:42 +02:00
|
|
|
if (member == NULL)
|
|
|
|
oom("class_member__new");
|
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
class__add_member(cu__current_class, member);
|
2006-10-28 23:22:42 +02:00
|
|
|
} else if (tag == DW_TAG_subrange_type)
|
2006-11-05 18:46:45 +01:00
|
|
|
cu__current_class->nr_entries = attr_upper_bound(die);
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
else if (tag == DW_TAG_variable) {
|
|
|
|
uint64_t abstract_origin = attr_numeric(die,
|
|
|
|
DW_AT_abstract_origin);
|
|
|
|
struct variable *variable;
|
|
|
|
|
|
|
|
variable = variable__new(name, cu_offset,
|
|
|
|
type, abstract_origin);
|
|
|
|
if (variable == NULL)
|
|
|
|
oom("variable__new");
|
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
class__add_variable(cu__current_class, variable);
|
[CLASSES]: Introduce struct variable
To represent DW_TAG_variable, for now all the variables in all the lexical
blocks, in addition to the top level function variables are in this list, next
step is to add support for DW_TAG_lexical_block, with support for nesting, and
to associate variables to the right place, be it the function itself (first,
implicit lexical block) or to the lexical blocks they belong too, this will be
useful for calculating stack usage.
So, with what we have now pfunct can do this:
[acme@newtoy guinea_pig-2.6]$ pfunct --variables net/ipv4/built-in.o tcp_v4_remember_stamp
/* net/ipv4/tcp_ipv4.c:1197 */
int tcp_v4_remember_stamp(struct sock * sk);
{
/* variables in tcp_v4_remember_stamp: */
struct inet_sock * inet;
struct tcp_sock * tp;
struct rtable * rt;
struct inet_peer * peer;
int release_it;
}
[acme@newtoy guinea_pig-2.6]$
That is already useful when you don't have the sources, huh? :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-05 04:31:41 +01:00
|
|
|
cu__add_variable(current_cu, variable);
|
|
|
|
} else if (tag == DW_TAG_label)
|
2006-11-05 18:46:45 +01:00
|
|
|
++cu__current_class->nr_labels;
|
2006-11-03 16:41:19 +01:00
|
|
|
else if (tag == DW_TAG_inlined_subroutine) {
|
2006-11-03 18:32:32 +01:00
|
|
|
Dwarf_Addr high_pc, low_pc;
|
|
|
|
if (dwarf_highpc(die, &high_pc)) high_pc = 0;
|
|
|
|
if (dwarf_lowpc(die, &low_pc)) low_pc = 0;
|
2006-11-03 16:41:19 +01:00
|
|
|
const uintmax_t type = attr_numeric(die, DW_AT_abstract_origin);
|
2006-11-03 18:32:32 +01:00
|
|
|
uint64_t size = high_pc - low_pc;
|
2006-11-03 16:41:19 +01:00
|
|
|
struct inline_expansion *exp;
|
2006-11-03 18:32:32 +01:00
|
|
|
|
2006-11-03 16:41:19 +01:00
|
|
|
if (size == 0) {
|
|
|
|
Dwarf_Addr base, start, end;
|
|
|
|
ptrdiff_t offset = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
offset = dwarf_ranges(die, offset, &base, &start, &end);
|
|
|
|
if (offset <= 0)
|
|
|
|
break;
|
|
|
|
size += end - start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exp = inline_expansion__new(type, size);
|
|
|
|
if (exp == NULL)
|
|
|
|
oom("inline_expansion__new");
|
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
class__add_inline_expansion(cu__current_class, exp);
|
2006-11-03 16:41:19 +01:00
|
|
|
goto next_sibling;
|
|
|
|
} else if (tag == DW_TAG_lexical_block) {
|
2006-11-02 19:53:39 +01:00
|
|
|
/*
|
|
|
|
* Not handled right now,
|
|
|
|
* will be used for stack size calculation
|
|
|
|
*/
|
|
|
|
} else {
|
2006-11-05 03:46:22 +01:00
|
|
|
uint64_t size = attr_numeric(die, DW_AT_byte_size);
|
2006-10-30 17:37:04 +01:00
|
|
|
const unsigned short inlined = attr_numeric(die, DW_AT_inline);
|
2006-11-03 18:32:32 +01:00
|
|
|
Dwarf_Addr high_pc, low_pc;
|
|
|
|
if (dwarf_highpc(die, &high_pc)) high_pc = 0;
|
|
|
|
if (dwarf_lowpc(die, &low_pc)) low_pc = 0;
|
2006-10-30 17:37:04 +01:00
|
|
|
const char *decl_file = dwarf_decl_file(die);
|
2006-11-11 00:18:06 +01:00
|
|
|
int decl_line = 0;
|
2006-10-30 17:37:04 +01:00
|
|
|
|
|
|
|
dwarf_decl_line(die, &decl_line);
|
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
if (cu__current_class != NULL)
|
|
|
|
cu__add_class(current_cu, cu__current_class);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
2006-11-05 18:46:45 +01:00
|
|
|
cu__current_class = class__new(tag, cu_offset,
|
2006-10-28 23:22:42 +02:00
|
|
|
type, name, size,
|
2006-10-29 02:40:35 +02:00
|
|
|
decl_file, decl_line,
|
2006-10-30 18:22:39 +01:00
|
|
|
inlined, low_pc, high_pc);
|
2006-11-05 18:46:45 +01:00
|
|
|
if (cu__current_class == NULL)
|
2006-10-28 23:22:42 +02:00
|
|
|
oom("class__new");
|
|
|
|
}
|
|
|
|
|
2006-11-10 22:19:58 +01:00
|
|
|
children:
|
2006-10-28 23:22:42 +02:00
|
|
|
if (dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0)
|
2006-11-05 18:46:45 +01:00
|
|
|
cu__process_die(dwarf, &child);
|
2006-11-03 16:41:19 +01:00
|
|
|
next_sibling:
|
2006-10-28 23:22:42 +02:00
|
|
|
if (dwarf_siblingof (die, die) == 0)
|
2006-11-05 18:46:45 +01:00
|
|
|
cu__process_die(dwarf, die);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
int cus__load(struct cus *self)
|
2006-10-28 23:22:42 +02:00
|
|
|
{
|
|
|
|
Dwarf_Off offset, last_offset, abbrev_offset;
|
|
|
|
uint8_t addr_size, offset_size;
|
|
|
|
size_t hdr_size;
|
|
|
|
Dwarf *dwarf;
|
|
|
|
int err = -1;
|
2006-11-11 19:31:04 +01:00
|
|
|
int fd = open(self->filename, O_RDONLY);
|
2006-10-28 23:22:42 +02:00
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
dwarf = dwarf_begin(fd, DWARF_C_READ);
|
|
|
|
if (dwarf == NULL)
|
|
|
|
goto out_close;
|
|
|
|
|
|
|
|
offset = last_offset = 0;
|
|
|
|
while (dwarf_nextcu(dwarf, offset, &offset, &hdr_size,
|
|
|
|
&abbrev_offset, &addr_size, &offset_size) == 0) {
|
|
|
|
Dwarf_Die die;
|
|
|
|
|
|
|
|
if (dwarf_offdie(dwarf, last_offset + hdr_size, &die) != NULL) {
|
2006-11-03 18:38:43 +01:00
|
|
|
Dwarf_Attribute name;
|
|
|
|
current_cu = cu__new(current_cu_id,
|
|
|
|
attr_string(&die, DW_AT_name,
|
|
|
|
&name));
|
2006-10-31 20:12:42 +01:00
|
|
|
if (current_cu == NULL)
|
|
|
|
oom("cu__new");
|
|
|
|
++current_cu_id;
|
2006-11-05 18:46:45 +01:00
|
|
|
cu__process_die(dwarf, &die);
|
2006-11-11 19:31:04 +01:00
|
|
|
cus__add(self, current_cu);
|
2006-10-28 23:22:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
last_offset = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
dwarf_end(dwarf);
|
|
|
|
err = 0;
|
|
|
|
out_close:
|
|
|
|
close(fd);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
2006-11-11 19:31:04 +01:00
|
|
|
|
|
|
|
struct cus *cus__new(const char *filename)
|
|
|
|
{
|
|
|
|
struct cus *self = malloc(sizeof(*self));
|
|
|
|
|
|
|
|
if (self != NULL) {
|
|
|
|
INIT_LIST_HEAD(&self->cus);
|
|
|
|
self->filename = strings__add(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|