[Ltrace-devel] [PATCH 10/11] structs

Steve Fink sphink at gmail.com
Sat Aug 5 23:58:32 UTC 2006


-------------- next part --------------
From d956781b90b4b51760ad2df83bddab462957bfe7 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 21:38:50 -0700
Subject: [PATCH 10/11] struct parameters

Allow parameters to be pointers to structs, which themselves can contain
(nearly) any other type, including other structs or pointers to structs.
Lengths for strings and arrays can be read out of the struct itself.
---
 ChangeLog                              |    1 
 display_args.c                         |   83 +++++++++++++++++++----
 etc/ltrace.conf                        |   40 +++++++++++
 ltrace.h                               |   12 +++
 read_config_file.c                     |  114 +++++++++++++++++++++++++++++++-
 testsuite/ltrace.main/parameters-lib.c |   36 ++++++++++
 testsuite/ltrace.main/parameters.c     |   33 +++++++++
 testsuite/ltrace.main/parameters.conf  |    1 
 testsuite/ltrace.main/parameters.exp   |    2 +
 9 files changed, 304 insertions(+), 18 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3236d86..d4d30dd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* various: Implement struct parameters
 	* display_args.c: Implement parameters that point to arrays
 	* options.c: Add -A option for giving max # array elts
 	* read-config-file.c: Implement typedef (aliased parameter types)
diff --git a/display_args.c b/display_args.c
index f91267c..e8b20e1 100644
--- a/display_args.c
+++ b/display_args.c
@@ -15,27 +15,35 @@ static int display_char(int what);
 static int display_string(enum tof type, struct process *proc,
 			  void* addr, size_t maxlen);
 static int display_value(enum tof type, struct process *proc,
-			 long value, arg_type_info *info);
+			 long value, arg_type_info *info,
+			 void *st, arg_type_info* st_info);
 static int display_unknown(enum tof type, struct process *proc, long value);
 static int display_format(enum tof type, struct process *proc, int arg_num);
 
 static int string_maxlength = INT_MAX;
 static int array_maxlength = INT_MAX;
 
-static long get_length(enum tof type, struct process *proc, int len_spec)
+static long get_length(enum tof type, struct process *proc, int len_spec,
+		       void *st, arg_type_info* st_info)
 {
+    long len;
     if (len_spec > 0)
 	return len_spec;
+    if (type == LT_TOF_STRUCT) {
+	umovelong(proc, st + st_info->u.struct_info.offset[-len_spec-1], &len);
+	return len;
+    }
     return gimme_arg(type, proc, -len_spec - 1);
 }
 
 static int display_ptrto(enum tof type, struct process *proc, long item,
-			 arg_type_info * info)
+			 arg_type_info * info,
+			 void *st, arg_type_info* st_info)
 {
     arg_type_info temp;
     temp.type = ARGTYPE_POINTER;
     temp.u.ptr_info.info = info;
-    return display_value(type, proc, item, &temp);
+    return display_value(type, proc, item, &temp, st, st_info);
 }
 
 /*
@@ -47,7 +55,8 @@ static int display_ptrto(enum tof type, 
  * pointer to that region of memory.
  */
 static int display_arrayptr(enum tof type, struct process *proc,
-			    void *addr, arg_type_info * info)
+			    void *addr, arg_type_info * info,
+			    void *st, arg_type_info* st_info)
 {
     int len = 0;
     int i;
@@ -56,7 +65,8 @@ static int display_arrayptr(enum tof typ
     if (addr == NULL)
 	return fprintf(output, "NULL");
 
-    array_len = get_length(type, proc, info->u.array_info.len_spec);
+    array_len = get_length(type, proc, info->u.array_info.len_spec,
+			   st, st_info);
     len += fprintf(output, "[ ");
     for (i = 0; i < opt_A && i < array_maxlength && i < array_len; i++) {
 	arg_type_info *elt_type = info->u.array_info.elt_type;
@@ -66,7 +76,7 @@ static int display_arrayptr(enum tof typ
 	if (opt_d)
 	    len += fprintf(output, "%p=", addr);
 	len +=
-	    display_ptrto(type, proc, (long) addr, elt_type);
+	    display_ptrto(type, proc, (long) addr, elt_type, st, st_info);
 	addr += elt_size;
     }
     if (i < array_len)
@@ -75,21 +85,57 @@ static int display_arrayptr(enum tof typ
     return len;
 }
  
+/* addr - A pointer to the beginning of the memory region occupied by
+ *        the struct (aka a pointer to the struct)
+ */
+static int display_structptr(enum tof type, struct process *proc,
+			     void *addr, arg_type_info * info)
+{
+    int i;
+    arg_type_info *field;
+    int len = 0;
+
+    if (addr == NULL)
+	return fprintf(output, "NULL");
+
+    len += fprintf(output, "{ ");
+    for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) {
+	if (i != 0)
+	    len += fprintf(output, ", ");
+	if (opt_d)
+	    len +=
+		fprintf(output, "%p=",
+			addr + info->u.struct_info.offset[i]);
+	len +=
+	    display_ptrto(LT_TOF_STRUCT, proc,
+			  (long) addr + info->u.struct_info.offset[i],
+			  field, addr, info);
+    }
+    len += fprintf(output, " }");
+
+    return len;
+}
+
 static int display_pointer(enum tof type, struct process *proc, long value,
-			   arg_type_info * info)
+			   arg_type_info * info,
+			   void *st, arg_type_info* st_info)
 {
     long pointed_to;
     arg_type_info *inner = info->u.ptr_info.info;
 
     if (inner->type == ARGTYPE_ARRAY) {
-	return display_arrayptr(type, proc, (void*) value, inner);
+	return display_arrayptr(type, proc, (void*) value, inner,
+				st, st_info);
+    } else if (inner->type == ARGTYPE_STRUCT) {
+	return display_structptr(type, proc, (void *) value, inner);
     } else {
 	if (value == 0)
 	    return fprintf(output, "NULL");
 	else if (umovelong(proc, (void *) value, &pointed_to) < 0)
 	    return fprintf(output, "?");
 	else
-	    return display_value(type, proc, pointed_to, inner);
+	    return display_value(type, proc, pointed_to, inner,
+				 st, st_info);
     }
 }
 
@@ -110,9 +156,15 @@ static int display_enum(enum tof type, s
    proc - information about the traced process
    value - the value to display
    info - the description of the type to display
+   st - if the current value is a struct member, the address of the struct
+   st_info - type of the above struct
+
+   Those last two parameters are used for structs containing arrays or
+   strings whose length is given by another structure element.
 */
 int display_value(enum tof type, struct process *proc,
-                  long value, arg_type_info *info)
+                  long value, arg_type_info *info,
+		  void *st, arg_type_info* st_info)
 {
 	int tmp;
 
@@ -165,13 +217,16 @@ int display_value(enum tof type, struct 
 	case ARGTYPE_STRING_N:
 		return display_string(type, proc, (void*) value,
 				      get_length(type, proc,
-						 info->u.string_n_info.size_spec));
+						 info->u.string_n_info.size_spec, st, st_info));
 	case ARGTYPE_ARRAY:
 		return fprintf(output, "<array without address>");
         case ARGTYPE_ENUM:
 		return display_enum(type, proc, info, value);
+	case ARGTYPE_STRUCT:
+		return fprintf(output, "<struct without address>");
 	case ARGTYPE_POINTER:
-		return display_pointer(type, proc, value, info);
+		return display_pointer(type, proc, value, info,
+				       st, st_info);
  	case ARGTYPE_UNKNOWN:
 	default:
 		return display_unknown(type, proc, value);
@@ -189,7 +244,7 @@ int display_arg(enum tof type, struct pr
 	return display_format(type, proc, arg_num);
     } else {
 	arg = gimme_arg(type, proc, arg_num);
-	return display_value(type, proc, arg, info);
+	return display_value(type, proc, arg, info, NULL, NULL);
     }
 }
 
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index fa901fc..1fe808e 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -21,6 +21,7 @@
 ; format	== ((const char *), ...)	[printf() like]		[TODO]
 ; string	== (char *)
 ; string[argN]  == (char *)		[N>0]	[show only up to (arg N) bytes]
+; string[eltN]  == (char *)		[N>0]	[show only up to (elt N) bytes]
 ; string[retval] == (char *)			[show only up to (return val) bytes]
 ; string[arg0]	== (char *)			[same as string[retval]]
 ; string[N]     == (char *)             [N>0]   [show only up to N bytes]
@@ -29,12 +30,18 @@
 ; enum (key=value,key=value,...)		[enumeration, see below]
 ; array(type,argN)
 ;		== (type[SIZE])			[array of (arg N) elements]
+; array(type,eltN)
+;		== (type[SIZE])			[array of (struct element N) elements]
 ; array(type,N)	== (type[N])			[array of N elements]
-
+; struct(type,type,...)
+;		== (struct {...})		[struct of several types]
+;
 ; Backwards-compatibility:
 ; string0	== (char *)			[same as string[retval]]
 ; stringN	== (char *)		[N>0]	[same as string[argN]]
 
+
+
 ; Typedefs
 ;
 ; To make it easier to specify argument lists, you can use 'typedef'
@@ -56,6 +63,37 @@
 ; NOTE: Uses of array(...) alone are very rare. You almost always
 ; want array(...)*. The exceptions are when you have a fixed-size
 ; array.
+;
+; Structs
+;
+; NOTE: Uses of struct(...) alone are very rare. You almost always
+; want struct(...)* (a pointer to a struct) anyway. Most compilers
+; pass structs as pointers anyway, and those that don't are not yet
+; supported. The one time when you want to use a non-pointer
+; struct(...) type are when you have an array of structs, or a struct
+; containing another struct.
+;
+; For example, if you have
+;   struct s1 {
+;	int y_size;
+;	int * y;
+;	int z[3];
+;	struct { char c; } a;
+;	struct { char c; } * b;
+;   }
+; and a function
+;   void f(struct s1*)
+; then the corresponding ltrace spec is
+;   void f(struct(int,array(int,elt0),array(int,3),struct(char),struct(char)*)*)
+; which, formatted similarly to the C declaration, looks like
+;   void f(struct(
+;                 int,
+;                 array(int,elt0),
+;                 array(int,3),
+;                 struct(char),
+;                 struct(char)*
+;                )*
+;         )
 
 
 ; arpa/inet.h
diff --git a/ltrace.h b/ltrace.h
index fe5fd87..2e0d01e 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -47,6 +47,7 @@ enum arg_type {
 	ARGTYPE_STRING_N,	/* String of known maxlen */
         ARGTYPE_ARRAY,		/* Series of values in memory */
         ARGTYPE_ENUM,		/* Enumeration */
+        ARGTYPE_STRUCT,		/* Structure of values */
         ARGTYPE_IGNORE,		/* Leave parameter blank */
         ARGTYPE_POINTER,	/* Pointer to some other type */
         ARGTYPE_COUNT		/* number of ARGTYPE_* values */
@@ -74,6 +75,14 @@ typedef struct arg_type_info_t {
 	    int size_spec;
 	} string_n_info;
 
+	// ARGTYPE_STRUCT
+	struct {
+	    struct arg_type_info_t **fields;	// NULL-terminated
+	    size_t *gap;
+	    size_t *offset;
+	    size_t size;
+	} struct_info;
+
 	// ARGTYPE_POINTER
 	struct {
 	    struct arg_type_info_t *info;
@@ -86,7 +95,8 @@ enum tof {
 	LT_TOF_FUNCTION,	/* A real library function */
 	LT_TOF_FUNCTIONR,	/* Return from a real library function */
 	LT_TOF_SYSCALL,		/* A syscall */
-	LT_TOF_SYSCALLR		/* Return from a syscall */
+	LT_TOF_SYSCALLR,	/* Return from a syscall */
+        LT_TOF_STRUCT		/* Not a function; read args from struct */
 };
 
 struct function {
diff --git a/read_config_file.c b/read_config_file.c
index 51d4d85..ec1a4a8 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -19,6 +19,8 @@ static arg_type_info *parse_type(char **
 
 struct function *list_of_functions = NULL;
 
+/* Map of strings to type names. These do not need to be in any
+ * particular order */
 static struct list_of_pt_t {
 	char *name;
 	enum arg_type pt;
@@ -39,11 +41,15 @@ static struct list_of_pt_t {
 	"format", ARGTYPE_FORMAT}, {
 	"string", ARGTYPE_STRING}, {
 	"array", ARGTYPE_ARRAY}, {
+	"struct", ARGTYPE_STRUCT}, {
 	"enum", ARGTYPE_ENUM}, {
 	"ignore", ARGTYPE_IGNORE}, {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
 };
 
+/* Array of singleton objects for each of the types. The order in this
+ * array must exactly match the list of enumerated values in
+ * ltrace.h */
 static arg_type_info arg_type_singletons[] = {
 	{ ARGTYPE_VOID },
 	{ ARGTYPE_INT },
@@ -62,6 +68,7 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_STRING_N },
 	{ ARGTYPE_ARRAY },
 	{ ARGTYPE_ENUM },
+	{ ARGTYPE_STRUCT },
 	{ ARGTYPE_IGNORE },
 	{ ARGTYPE_POINTER },
 	{ ARGTYPE_UNKNOWN }
@@ -166,6 +173,7 @@ static int simple_type(enum arg_type at)
     case ARGTYPE_STRING_N:
     case ARGTYPE_ARRAY:
     case ARGTYPE_ENUM:
+    case ARGTYPE_STRUCT:
 	return 0;
 
     default:
@@ -178,8 +186,8 @@ static int parse_int(char **str)
     char *end;
     long n = strtol(*str, &end, 0);
     if (end == *str) {
-	output_line(0, "Syntax error in `%s', line %d: Bad number",
-		    filename, line_no);
+	output_line(0, "Syntax error in `%s', line %d: Bad number (%s)",
+		    filename, line_no, *str);
 	error_count++;
 	return 0;
     }
@@ -209,6 +217,9 @@ static int parse_argnum(char **str)
     if (strncmp(*str, "arg", 3) == 0) {
 	(*str) += 3;
 	multiplier = -1;
+    } else if (strncmp(*str, "elt", 3) == 0) {
+	(*str) += 3;
+	multiplier = -1;
     } else if (strncmp(*str, "retval", 6) == 0) {
 	(*str) += 6;
 	return 0;
@@ -289,6 +300,8 @@ static size_t arg_sizeof(arg_type_info *
 	return sizeof(float);
     } else if (arg->type == ARGTYPE_ENUM) {
 	return sizeof(int);
+    } else if (arg->type == ARGTYPE_STRUCT) {
+	return arg->u.struct_info.size;
     } else if (arg->type == ARGTYPE_ARRAY) {
 	if (arg->u.array_info.len_spec > 0)
 	    return arg->u.array_info.len_spec * arg->u.array_info.elt_size;
@@ -299,6 +312,63 @@ static size_t arg_sizeof(arg_type_info *
     }
 }
 
+/* I'm sure this isn't completely correct, but just try to get most of
+ * them right for now. */
+#undef alignof
+#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
+static void align_struct(arg_type_info* info)
+{
+    struct {
+	char c;
+	int i;
+    } ci;
+    struct {
+	struct {
+	    char c;
+	} s;
+	int i;
+    } cis;
+
+    size_t int_alignment = alignof(i, ci);
+    size_t whole_struct_alignment = alignof(i, cis);
+
+    size_t offset;
+    size_t gap;
+    int i;
+
+    if (info->u.struct_info.size != 0)
+	return;			// Already done
+
+    // The gap array isn't actually needed anymore, because the
+    // offset can be used for everything.
+
+    // 1. Add internal padding
+    offset = 0;
+    for (i = 0; info->u.struct_info.fields[i] != NULL; i++) {
+	arg_type_info *field = info->u.struct_info.fields[i];
+	info->u.struct_info.offset[i] = offset;
+	offset += arg_sizeof(field);
+
+	if (offset % int_alignment != 0) {
+	    gap = int_alignment - offset % int_alignment;
+	    info->u.struct_info.gap[i] = gap;
+	    offset += gap;
+	}
+    }
+
+    // 2. Add padding at end of entire struct
+    for (i = 0; info->u.struct_info.fields[i] != NULL; i++);
+    if (i == 0)
+	return;
+    if (offset % whole_struct_alignment != 0) {
+	gap = whole_struct_alignment - offset % whole_struct_alignment;
+	info->u.struct_info.gap[i - 1] = gap;
+	offset += gap;
+    }
+
+    info->u.struct_info.size = offset;
+}
+
 static arg_type_info *parse_nonpointer_type(char **str)
 {
 	arg_type_info *simple;
@@ -432,6 +502,46 @@ static arg_type_info *parse_nonpointer_t
 	    (*str)++;		// Skip past closing ]
 	    return info;
 
+	// Syntax: struct ( type,type,type,... )
+    case ARGTYPE_STRUCT:{
+	    int field_num = 0;
+	    (*str)++;		// Get past open paren
+	    info->u.struct_info.fields =
+		malloc((MAX_ARGS + 1) * sizeof(void *));
+	    info->u.struct_info.gap =
+		malloc((MAX_ARGS + 1) * sizeof(size_t));
+	    info->u.struct_info.offset =
+		malloc((MAX_ARGS + 1) * sizeof(size_t));
+	    info->u.struct_info.size = 0;
+	    eat_spaces(str); // Empty arg list with whitespace inside
+	    while (**str && **str != ')') {
+		if (field_num == MAX_ARGS) {
+		    output_line(0,
+				"Error in `%s', line %d: Too many structure elements",
+				filename, line_no);
+		    error_count++;
+		    return NULL;
+		}
+		eat_spaces(str);
+		if (field_num != 0) {
+		    (*str)++;	// Get past comma
+		    eat_spaces(str);
+		}
+		info->u.struct_info.gap[field_num] = 0;
+		if ((info->u.struct_info.fields[field_num++] =
+		     parse_type(str)) == NULL)
+		    return NULL;
+
+		// Must trim trailing spaces so the check for
+		// the closing paren is simple
+		eat_spaces(str);
+	    }
+	    (*str)++;		// Get past closing paren
+	    info->u.struct_info.fields[field_num] = NULL;
+	    align_struct(info);
+	    return info;
+	}
+
 	default:
 		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
 			    filename, line_no);
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index a45cd2e..26e4c3b 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -79,3 +79,39 @@ void func_arrayf(float* a, int N)
 	printf("%f ", a[i]);
     printf("\n");
 }
+
+struct test_struct {
+    int simple;
+    int alen;
+    int slen;
+    struct { int a; int b; }* array;
+    struct { int a; int b; } seq[3];
+    char* str;
+    char* outer_str;
+};
+
+void func_struct(struct test_struct* x)
+{
+    char buf[100];
+    int i;
+
+    printf("struct: ");
+
+    printf("%d, [", x->simple);
+    for (i = 0; i < x->alen; i++) {
+	printf("%d/%d", x->array[i].a, x->array[i].b);
+	if (i < x->alen - 1)
+	    printf(" ");
+    }
+    printf("] [");
+    for (i = 0; i < 3; i++) {
+	printf("%d/%d", x->seq[i].a, x->seq[i].b);
+	if (i < 2)
+	    printf(" ");
+    }
+    printf("] ");
+
+    strncpy(buf, x->str, x->slen);
+    buf[x->slen] = '\0';
+    printf("%s\n", buf);
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index dbc8ace..d0fa483 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -24,6 +24,7 @@ void func_ushort(unsigned short, unsigne
 void func_float(float, float);
 void func_arrayi(int*, int);
 void func_arrayf(float*, int);
+void func_struct(void*);
 
 typedef enum {
   RED,
@@ -86,5 +87,37 @@ main ()
   func_arrayf(af, 8);
   func_arrayf(af, 2);
 
+  {
+    struct {
+      int simple;
+      int alen;
+      int slen;
+      struct { int a; int b; }* array;
+      struct { int a; int b; } seq[3];
+      char* str;
+    } x;
+
+    x.simple = 89;
+
+    x.alen = 2;
+    x.array = malloc(800);
+    x.array[0].a = 1;
+    x.array[0].b = 10;
+    x.array[1].a = 3;
+    x.array[1].b = 30;
+
+    x.seq[0].a = 4;
+    x.seq[0].b = 40;
+    x.seq[1].a = 5;
+    x.seq[1].b = 50;
+    x.seq[2].a = 6;
+    x.seq[2].b = 60;
+
+    x.slen = 3;
+    x.str = "123junk";
+
+    func_struct(&x);
+  }
+
   return 0;
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index 8216857..e8bd3e2 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -13,3 +13,4 @@ typedef color = enum (RED=0,GREEN=1,BLUE
 void func_typedef(color)
 void func_arrayi(array(int,arg2)*,ignore)
 void func_arrayf(array(float,arg2)*,ignore)
+void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*)
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index ee269e4..f07c296 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -70,3 +70,5 @@ set pattern "func_arrayf(. 10.10*, 11.10
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "exited (status 0)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_struct({ 89, 2, 3, . { 1, 10 }, { 3, 30 } ., . { 4, 40 }, { 5, 50 }, { 6, 60 } ., \\\"123\\\" })"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1


More information about the Ltrace-devel mailing list