Allow forward declarations of structs
Now ltrace can display singly-linked lists. Recursion detection was added
to format_pointer. It relies on the fact that ltrace is not multi-threaded
and doesn't need per-process or per-thread cache of already-displayed
values, because we display one value at a time anyway.
Several test cases added.
diff --git a/lens_default.c b/lens_default.c
index a2233b8..f3e328f 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -27,6 +27,7 @@
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
+#include <string.h>
#include "proc.h"
#include "lens_default.h"
@@ -250,6 +251,36 @@
if (value_is_zero(value, arguments))
return fprintf(stream, "nil");
+ /* The following is for detecting recursion. We keep track of
+ * the values that were already displayed. Each time a
+ * pointer should be dereferenced, we compare its value to the
+ * value of each of the pointers dereferenced so far. If one
+ * of them matches, instead of recursing, we just printf which
+ * superstructure this pointer recurses to. */
+ static struct vect pointers = {};
+ if (pointers.elt_size == 0)
+ VECT_INIT(&pointers, struct value *);
+
+ size_t len = vect_size(&pointers);
+ size_t i;
+ for (i = len; i-- > 0 ;) {
+ struct value **old = VECT_ELEMENT(&pointers, struct value *, i);
+ int rc = value_equal(value, *old, arguments);
+ if (rc < 0)
+ return -1;
+ if (rc > 0) {
+ size_t reclevel = len - i - 1;
+ char buf[reclevel + 1];
+ memset(buf, '^', sizeof buf);
+ buf[reclevel] = 0;
+ return fprintf(stream, "recurse%s", buf);
+ }
+ }
+
+ /* OK, not a recursion. Remember this value for tracking. */
+ if (VECT_PUSHBACK(&pointers, &value) < 0)
+ return -1;
+
struct value element;
int o;
if (value_init_deref(&element, value) < 0) {
@@ -260,6 +291,7 @@
value_destroy(&element);
done:
+ vect_popback(&pointers);
return o;
}
diff --git a/read_config_file.c b/read_config_file.c
index b93cec6..18fc156 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -43,14 +43,18 @@
static int line_no;
static char *filename;
+struct typedef_node_t;
static struct arg_type_info *parse_nonpointer_type(char **str,
struct param **extra_param,
- size_t param_num, int *ownp);
+ size_t param_num, int *ownp,
+ struct typedef_node_t *td);
static struct arg_type_info *parse_type(char **str, struct param **extra_param,
- size_t param_num, int *ownp);
+ size_t param_num, int *ownp,
+ struct typedef_node_t *in_typedef);
static struct arg_type_info *parse_lens(char **str, struct param **extra_param,
- size_t param_num, int *ownp);
+ size_t param_num, int *ownp,
+ struct typedef_node_t *in_typedef);
static int parse_enum(char **str, struct arg_type_info **retp, int *ownp);
Function *list_of_functions = NULL;
@@ -359,16 +363,17 @@
char *name;
struct arg_type_info *info;
int own_type;
+ int forward : 1;
struct typedef_node_t *next;
} *typedefs = NULL;
-static struct arg_type_info *
+static struct typedef_node_t *
lookup_typedef(const char *name)
{
struct typedef_node_t *node;
for (node = typedefs; node != NULL; node = node->next)
if (strcmp(name, node->name) == 0)
- return node->info;
+ return node;
return NULL;
}
@@ -387,18 +392,30 @@
*str += len;
buf[len] = 0;
- return lookup_typedef(buf);
+ struct typedef_node_t *td = lookup_typedef(buf);
+ if (td == NULL)
+ return NULL;
+ return td->info;
+}
+
+static void
+insert_typedef(struct typedef_node_t *td)
+{
+ if (td == NULL)
+ return;
+ td->next = typedefs;
+ typedefs = td;
}
static struct typedef_node_t *
-insert_typedef(char *name, struct arg_type_info *info, int own_type)
+new_typedef(char *name, struct arg_type_info *info, int own_type)
{
struct typedef_node_t *binding = malloc(sizeof(*binding));
binding->name = name;
binding->info = info;
binding->own_type = own_type;
- binding->next = typedefs;
- typedefs = binding;
+ binding->forward = 0;
+ binding->next = NULL;
return binding;
}
@@ -409,10 +426,15 @@
eat_spaces(str);
char *name = parse_ident(str);
- struct arg_type_info *info = lookup_typedef(name);
- if (info != NULL) {
+ /* Look through the typedef list whether we already have a
+ * forward of this type. If we do, it must be forward
+ * structure. */
+ struct typedef_node_t *forward = lookup_typedef(name);
+ if (forward != NULL
+ && (forward->info->type != ARGTYPE_STRUCT
+ || !forward->forward)) {
report_error(filename, line_no,
- "Redefinition of typedef '%s'\n", name);
+ "Redefinition of typedef '%s'", name);
return;
}
@@ -422,11 +444,42 @@
return;
eat_spaces(str);
- // Parse the type
- int own;
- info = parse_type(str, NULL, 0, &own);
+ struct typedef_node_t *this_td = new_typedef(name, NULL, 0);
+ this_td->info = parse_type(str, NULL, 0, &this_td->own_type, this_td);
- insert_typedef(name, info, own);
+ if (this_td->info == NULL) {
+ free(this_td);
+ return;
+ }
+
+ if (forward == NULL) {
+ insert_typedef(this_td);
+ return;
+ }
+
+ /* If we are defining a forward, make sure the definition is a
+ * structure as well. */
+ if (this_td->info->type != ARGTYPE_STRUCT) {
+ report_error(filename, line_no,
+ "Definition of forward '%s' must be a structure.",
+ name);
+ if (this_td->own_type) {
+ type_destroy(this_td->info);
+ free(this_td->info);
+ }
+ return;
+ }
+
+ /* Now move guts of the actual type over to the
+ * forward type. We can't just move pointers around,
+ * because references to forward must stay intact. */
+ assert(this_td->own_type);
+ type_destroy(forward->info);
+ *forward->info = *this_td->info;
+ forward->forward = 0;
+ free(this_td->info);
+ free(name);
+ free(this_td);
}
static void
@@ -446,9 +499,26 @@
/* Syntax: struct ( type,type,type,... ) */
static int
-parse_struct(char **str, struct arg_type_info *info)
+parse_struct(char **str, struct arg_type_info *info,
+ struct typedef_node_t *in_typedef)
{
eat_spaces(str);
+
+ if (**str == ';') {
+ if (in_typedef == NULL) {
+ report_error(filename, line_no,
+ "Forward struct can be declared only "
+ "directly after a typedef.");
+ return -1;
+ }
+
+ /* Forward declaration is currently handled as an
+ * empty struct. */
+ type_init_struct(info);
+ in_typedef->forward = 1;
+ return 0;
+ }
+
if (parse_char(str, '(') < 0)
return -1;
@@ -469,7 +539,8 @@
eat_spaces(str);
int own;
- struct arg_type_info *field = parse_lens(str, NULL, 0, &own);
+ struct arg_type_info *field = parse_lens(str, NULL, 0, &own,
+ NULL);
if (field == NULL || type_struct_add(info, field, own)) {
type_destroy(info);
return -1;
@@ -536,7 +607,7 @@
free(info);
eat_spaces(str);
- info = parse_type(str, NULL, 0, ownp);
+ info = parse_type(str, NULL, 0, ownp, NULL);
if (info == NULL)
goto fail;
@@ -681,7 +752,7 @@
eat_spaces(str);
int own;
- struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own);
+ struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own, NULL);
if (elt_info == NULL)
return -1;
@@ -718,7 +789,7 @@
if (**str == '[') {
parse_char(str, '[');
eat_spaces(str);
- *retp = parse_nonpointer_type(str, NULL, 0, ownp);
+ *retp = parse_nonpointer_type(str, NULL, 0, ownp, 0);
if (*retp == NULL)
return -1;
@@ -809,7 +880,7 @@
static struct arg_type_info *
parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
- int *ownp)
+ int *ownp, struct typedef_node_t *in_typedef)
{
enum arg_type type;
if (parse_arg_type(str, &type) < 0) {
@@ -828,8 +899,6 @@
return NULL;
}
- int (*parser) (char **, struct arg_type_info *) = NULL;
-
/* For some types that's all we need. */
switch (type) {
case ARGTYPE_VOID:
@@ -846,11 +915,7 @@
return type_get_simple(type);
case ARGTYPE_ARRAY:
- parser = parse_array;
- break;
-
case ARGTYPE_STRUCT:
- parser = parse_struct;
break;
case ARGTYPE_POINTER:
@@ -868,9 +933,16 @@
}
*ownp = 1;
- if (parser(str, info) < 0) {
- free(info);
- return NULL;
+ if (type == ARGTYPE_ARRAY) {
+ if (parse_array(str, info) < 0) {
+ fail:
+ free(info);
+ return NULL;
+ }
+ } else {
+ assert(type == ARGTYPE_STRUCT);
+ if (parse_struct(str, info, in_typedef) < 0)
+ goto fail;
}
return info;
@@ -901,10 +973,12 @@
}
static struct arg_type_info *
-parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp)
+parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp,
+ struct typedef_node_t *in_typedef)
{
struct arg_type_info *info
- = parse_nonpointer_type(str, extra_param, param_num, ownp);
+ = parse_nonpointer_type(str, extra_param,
+ param_num, ownp, in_typedef);
if (info == NULL)
return NULL;
@@ -932,7 +1006,8 @@
}
static struct arg_type_info *
-parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp)
+parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp,
+ struct typedef_node_t *in_typedef)
{
int own_lens;
struct lens *lens = name2lens(str, &own_lens);
@@ -956,7 +1031,8 @@
if (has_args) {
eat_spaces(str);
- info = parse_type(str, extra_param, param_num, ownp);
+ info = parse_type(str, extra_param, param_num, ownp,
+ in_typedef);
if (info == NULL) {
fail:
if (own_lens && lens != NULL)
@@ -1014,7 +1090,7 @@
char *ptr = str;
assert(str != NULL);
int own;
- struct arg_type_info *info = parse_lens(&ptr, NULL, 0, &own);
+ struct arg_type_info *info = parse_lens(&ptr, NULL, 0, &own, NULL);
assert(info != NULL);
free(str);
return info;
@@ -1045,7 +1121,8 @@
return NULL;
}
- fun->return_info = parse_lens(&str, NULL, 0, &fun->own_return_info);
+ fun->return_info = parse_lens(&str, NULL, 0,
+ &fun->own_return_info, NULL);
if (fun->return_info == NULL) {
err:
debug(3, " Skipping line %d", line_no);
@@ -1096,7 +1173,7 @@
int own;
struct arg_type_info *type
= parse_lens(&str, &extra_param,
- fun->num_params - have_stop, &own);
+ fun->num_params - have_stop, &own, NULL);
if (type == NULL) {
report_error(filename, line_no,
"unknown argument type");
@@ -1181,8 +1258,8 @@
info[0].u.ptr_info.info = &info[1];
info[1].type = ARGTYPE_VOID;
- insert_typedef(strdup("addr"), info, 0);
- insert_typedef(strdup("file"), info, 1);
+ insert_typedef(new_typedef(strdup("addr"), info, 0));
+ insert_typedef(new_typedef(strdup("file"), info, 1));
}
void
diff --git a/testsuite/ltrace.main/parameters2.exp b/testsuite/ltrace.main/parameters2.exp
index a7c32e0..6982de3 100644
--- a/testsuite/ltrace.main/parameters2.exp
+++ b/testsuite/ltrace.main/parameters2.exp
@@ -27,4 +27,69 @@
typedef aa = int;
}] -- true] "error" != 0
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+ typedef aa = struct;
+ typedef aa = int;
+}] -- true] "error" != 0
+
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+ typedef aa = struct;
+ typedef aa = struct(int);
+ typedef aa = struct(int);
+}] -- true] "error" != 0
+
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+ typedef aa = struct;
+ typedef aa = struct();
+ typedef aa = struct();
+}] -- true] "error" != 0
+
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+ typedef aa = struct(int, struct;);
+}] -- true] "error" != 0
+
+set libll [ltraceCompile libll.so [ltraceSource c {
+ struct xxx;
+ void ll(struct xxx *xxx) {}
+}]]
+
+set bin [ltraceCompile ll $libll [ltraceSource c {
+ struct xxx {
+ int i;
+ struct xxx *next;
+ };
+
+ void ll (struct xxx *xxx);
+ int main (int argc, char *argv[])
+ {
+ struct xxx a = { 1, 0 };
+ struct xxx b = { 2, &a };
+ struct xxx c = { 3, &b };
+ struct xxx d = { 4, &c };
+ ll (&d);
+
+ struct xxx e = { 1, 0 };
+ struct xxx f = { 2, &e };
+ e.next = &f;
+ ll (&f);
+
+ struct xxx g = { 1, &g };
+ ll (&g);
+
+ return 0;
+ }
+}]]
+
+set conf [ltraceSource conf {
+ typedef xxx = struct;
+ typedef xxx = struct(int, xxx*);
+ void ll(xxx*);
+}]
+
+ltraceMatch [ltraceRun -F $conf -e ll -- $bin] {
+ {{ll->ll\({ 4, { 3, { 2, { 1, nil } } } }\) *= <void>} == 1}
+ {{ll->ll\({ 2, { 1, recurse\^ } }\) *= <void>} == 1}
+ {{ll->ll\({ 1, recurse }\) *= <void>} == 1}
+}
+
ltraceDone