[PATCH] tracepoint: add new trace command "printf"[0] gdb

Hui Zhu
2011-01-03T16:29:50Z

[-- Attachment #1: Type: text/plain, Size: 2538 bytes --]

Hi,

I add a new patch add new trace command "printf".
This printf is same with the simple gdb command, it can do formatted
output. But it will happen in gdbserver part when it break by a
tracepoint.
Then the user can get the format value that he want in tracepint. It
will be more easy and clear to handle the bug sometimes.

For example:
(gdb) trace 16
During symbol reading, DW_AT_name missing from DW_TAG_base_type.
Tracepoint 1 at 0x4004c3: file 1.c, line 16.
(gdb) tvariable $c
Trace state variable $c created, with initial value 0.
(gdb) actions
Enter actions for tracepoint 1, one per line.
End with a line saying just "end".
>printf "%d 0x%lx %d\n",$c=$c+1,$rax,argc
>end
(gdb) target remote localhost:1234
(gdb) tstart
(gdb) c

gdbserver/gdbserver localhost:1234 ./a.out
Process ./a.out created; pid = 25804
Listening on port 1234
Remote debugging from host 127.0.0.1
1 0x7f2cb8711ec8 1
2 0x7f2cb8711ec8 2
3 0x7f2cb8711ec8 3
4 0x7f2cb8711ec8 4
5 0x7f2cb8711ec8 5
6 0x7f2cb8711ec8 6
7 0x7f2cb8711ec8 7

About the command part, I use the "printf" instead add a new commands
because the behavior of this command is same with printf. They will
use the same function string_printf(update from ui_printf) to parse
the command arg.

To support the printf command, I add a new agent expression 0x31
printf, the format for it is:
0x31(op_printf) + arg(1/0) + format string with end by 0x0.
The arg is the number of argument of printf. It will only be 1 (one
argument) or 0 (no argument). I make it cannot have more than one
argument because I cannot found a good way to handle va_list that send
arguments to vprintf.
The format string with end by 0x0 is the simple format string. It end
by 0x0 then the gdbserver can use it directly.

I didn't make the patch for doc because I am still not sure about
every part of this patch. When this patch is OK. I will make doc
patch for it.

Thanks,
Hui

2011-01-04 Hui Zhu <teawater@gmail.com>

* ax-gdb.c (gen_printf_expr_callback): New function.
* ax-general.c (ax_memcpy): New function.
(aop_map): Add new entry for "printf".
(ax_print): Handle "printf".
(ax_reqs): Ditto.
* ax.h (agent_op): Add aop_printf.
(ax_memcpy): Forward declare.
* printcmd.c (printf_callback): New typedef.
(string_printf): New function from ui_printf.
(ui_printf): Call string_printf.
(printf_command): Remove static.
* tracepoint.c (printf_command, gen_printf_expr_callback,
printf_callback, string_printf): Forward declares.
(validate_actionline, encode_actions_1): handle printf_command.

[-- Attachment #2: tp_print.txt --]
[-- Type: text/plain, Size: 10249 bytes --]

---
ax-gdb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ax-general.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++-------
ax.h | 3 +++
printcmd.c | 41 +++++++++++++++++++++++++++++++++++------
tracepoint.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 189 insertions(+), 13 deletions(-)

--- a/ax-gdb.c
+++ b/ax-gdb.c
@@ -2435,6 +2435,63 @@ gen_eval_for_expr (CORE_ADDR scope, stru
return ax;
}

+void
+gen_printf_expr_callback (char *fbuf, char **expp, struct bp_location *loc,
+ struct agent_expr *aexpr)
+{
+ if (expp)
+ {
+ struct cleanup *old_chain = NULL;
+ struct expression *expr = NULL;
+ union exp_element *pc;
+ struct axs_value value;
+
+ expr = parse_exp_1 (expp, block_for_pc (loc->address), 1);
+ old_chain = make_cleanup (free_current_contents, &expr);
+
+ pc = expr->elts;
+ trace_kludge = 0;
+ value.optimized_out = 0;
+ gen_expr (expr, &pc, aexpr, &value);
+
+
+ if (value.optimized_out)
+ error (_("value has been optimized out"));
+ switch (value.kind)
+ {
+ case axs_lvalue_memory:
+ {
+ int length = TYPE_LENGTH (check_typedef (value.type));
+ switch (length)
+ {
+ case 4:
+ ax_simple (aexpr, aop_ref32);
+ break;
+ case 8:
+ ax_simple (aexpr, aop_ref64);
+ break;
+ default:
+ error (_("size of value is not OK."));
+ break;
+ }
+ }
+ break;
+ case axs_lvalue_register:
+ ax_reg (aexpr, value.u.reg);
+ break;
+ }
+
+ do_cleanups (old_chain);
+ }
+
+ ax_simple (aexpr, aop_printf);
+ if (expp)
+ ax_simple (aexpr, 1);
+ else
+ ax_simple (aexpr, 0);
+ ax_memcpy (aexpr, fbuf, strlen (fbuf) + 1);
+}
+
static void
agent_command (char *exp, int from_tty)
{
--- a/ax-general.c
+++ b/ax-general.c
@@ -313,6 +313,14 @@ ax_tsv (struct agent_expr *x, enum agent
x->buf[x->len + 2] = (num) & 0xff;
x->len += 3;
}
+
+void
+ax_memcpy (struct agent_expr *x, const void *src, size_t n)
+{
+ grow_expr (x, n);
+ memcpy (x->buf + x->len, src, n);
+ x->len += n;
+}
\f

@@ -370,6 +378,7 @@ struct aop_map aop_map[] =
{"tracev", 2, 0, 0, 1}, /* 0x2e */
{0, 0, 0, 0, 0}, /* 0x2f */
{"trace16", 2, 0, 1, 1}, /* 0x30 */
+ {"printf", 0, 0, 0, 0}, /* 0x31 */
};

@@ -395,6 +404,7 @@ ax_print (struct ui_file *f, struct agen
for (i = 0; i < x->len;)
{
enum agent_op op = x->buf[i];
+ int op_size;

if (op >= (sizeof (aop_map) / sizeof (aop_map[0]))
|| !aop_map[op].name)
@@ -403,7 +413,19 @@ ax_print (struct ui_file *f, struct agen
i++;
continue;
}
- if (i + 1 + aop_map[op].op_size > x->len)
+ if (op == aop_printf)
+ {
+ if (i + 2 >= x->len)
+ {
+ fprintf_filtered (f, _("%3d <bad opcode %02x>\n"), i, op);
+ i++;
+ continue;
+ }
+ op_size = 1 + strlen (x->buf + i + 2) + 1;
+ }
+ else
+ op_size = aop_map[op].op_size;
+ if (i + 1 + op_size > x->len)
{
fprintf_filtered (f, _("%3d <incomplete opcode %s>\n"),
i, aop_map[op].name);
@@ -411,15 +433,15 @@ ax_print (struct ui_file *f, struct agen
}

fprintf_filtered (f, "%3d %s", i, aop_map[op].name);
- if (aop_map[op].op_size > 0)
+ if (op_size > 0)
{
fputs_filtered (" ", f);

print_longest (f, 'd', 0,
- read_const (x, i + 1, aop_map[op].op_size));
+ read_const (x, i + 1, op_size));
}
fprintf_filtered (f, "\n");
- i += 1 + aop_map[op].op_size;
+ i += 1 + op_size;

is_float = (op == aop_float);
}
@@ -487,6 +509,8 @@ ax_reqs (struct agent_expr *ax)
/* Pointer to a description of the present op. */
struct aop_map *op;

+ int op_size = 0, consumed = 0;
+
memset (targets, 0, ax->len * sizeof (targets[0]));
memset (boundary, 0, ax->len * sizeof (boundary[0]));

@@ -494,7 +518,7 @@ ax_reqs (struct agent_expr *ax)
ax->flaw = agent_flaw_none;
ax->max_data_size = 0;

- for (i = 0; i < ax->len; i += 1 + op->op_size)
+ for (i = 0; i < ax->len; i += 1 + op_size)
{
if (ax->buf[i] > (sizeof (aop_map) / sizeof (aop_map[0])))
{
@@ -510,7 +534,23 @@ ax_reqs (struct agent_expr *ax)
return;
}

- if (i + 1 + op->op_size > ax->len)
+ if (ax->buf[i] == aop_printf)
+ {
+ if (i + 2 >= ax->len)
+ {
+ ax->flaw = agent_flaw_incomplete_instruction;
+ return;
+ }
+ consumed = ax->buf[i + 1];
+ op_size = 1 + strlen (ax->buf + i + 2) + 1;
+ }
+ else
+ {
+ op_size = op->op_size;
+ consumed = op->consumed;
+ }
+
+ if (i + 1 + op_size > ax->len)
{
ax->flaw = agent_flaw_incomplete_instruction;
return;
@@ -528,7 +568,7 @@ ax_reqs (struct agent_expr *ax)
boundary[i] = 1;
heights[i] = height;

- height -= op->consumed;
+ height -= consumed;
if (height < ax->min_height)
ax->min_height = height;
height += op->produced;
--- a/ax.h
+++ b/ax.h
@@ -204,6 +204,7 @@ enum agent_op
aop_setv = 0x2d,
aop_tracev = 0x2e,
aop_trace16 = 0x30,
+ aop_printf = 0x31,
aop_last
};
\f
@@ -260,6 +261,8 @@ extern void ax_reg_mask (struct agent_ex

/* Assemble code to operate on a trace state variable. */
extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num);
+
+extern void ax_memcpy (struct agent_expr *x, const void *src, size_t n);
\f

/* Functions for printing out expressions, and otherwise debugging
--- a/printcmd.c
+++ b/printcmd.c
@@ -1963,10 +1963,13 @@ print_variable_and_value (const char *na
fprintf_filtered (stream, "\n");
}

-/* printf "printf format string" ARG to STREAM. */
+typedef void (printf_callback) (char *fbuf, char **expp,
+ struct bp_location *loc,
+ struct agent_expr *aexpr);

-static void
-ui_printf (char *arg, struct ui_file *stream)
+void
+string_printf (char *arg, struct ui_file *stream, printf_callback callback,
+ struct bp_location *loc, struct agent_expr *aexpr)
{
char *f = NULL;
char *s = arg;
@@ -2298,26 +2301,42 @@ ui_printf (char *arg, struct ui_file *st
/* Now, parse all arguments and evaluate them.
Store the VALUEs in VAL_ARGS. */

+ if (callback)
+ current_substring = substrings;
while (*s != '\0')
{
char *s1;

+ s1 = s;
if (nargs == allocated_args)
val_args = (struct value **) xrealloc ((char *) val_args,
(allocated_args *= 2)
* sizeof (struct value *));
- s1 = s;
- val_args[nargs] = parse_to_comma_and_eval (&s1);
+ if (callback)
+ {
+ if (nargs >= nargs_wanted)
+ error (_("Wrong number of arguments for specified "
+ "format-string"));
+ callback (current_substring, &s1, loc, aexpr);
+ current_substring += strlen (current_substring) + 1;
+ }
+ else
+ val_args[nargs] = parse_to_comma_and_eval (&s1);

nargs++;
s = s1;
if (*s == ',')
s++;
}
+ if (callback)
+ callback (last_arg, NULL, loc, aexpr);

if (nargs != nargs_wanted)
error (_("Wrong number of arguments for specified format-string"));

+ if (!stream)
+ goto after_print;
+
/* Now actually print them. */
current_substring = substrings;
for (i = 0; i < nargs; i++)
@@ -2672,12 +2691,22 @@ ui_printf (char *arg, struct ui_file *st
by default, which will warn here if there is no argument. */
fprintf_filtered (stream, last_arg, 0);
}
+
+after_print:
do_cleanups (old_cleanups);
}

-/* Implement the "printf" command. */
+/* printf "printf format string" ARG to STREAM. */

static void
+ui_printf (char *arg, struct ui_file *stream)
+{
+ string_printf (arg, stream, NULL, NULL, NULL);
+}
+
+/* Implement the "printf" command. */
+
+void
printf_command (char *arg, int from_tty)
{
ui_printf (arg, gdb_stdout);
--- a/tracepoint.c
+++ b/tracepoint.c
@@ -187,6 +187,15 @@ extern void send_disconnected_tracing_va
static void free_uploaded_tps (struct uploaded_tp **utpp);
static void free_uploaded_tsvs (struct uploaded_tsv **utsvp);

+extern void printf_command (char *arg, int from_tty);
+extern void gen_printf_expr_callback (char **expp, struct bp_location *loc,
+ struct agent_expr *aexpr);
+typedef void (printf_callback) (char **expp, struct bp_location *loc,
+ struct agent_expr *aexpr);
+extern void string_printf (char *arg, struct ui_file *stream,
+ printf_callback callback, struct bp_location *loc,
+ struct agent_expr *aexpr);
+

extern void _initialize_tracepoint (void);

@@ -719,6 +728,28 @@ validate_actionline (char **line, struct
error (_("while-stepping step count `%s' is malformed."), *line);
}

+ else if (cmd_cfunc_eq (c, printf_command))
+ {
+ char fbuf[101];
+
+ for (loc = t->loc; loc; loc = loc->next)
+ {
+ int nargs;
+ aexpr = new_agent_expr (loc->gdbarch, loc->address);
+ old_chain = make_cleanup_free_agent_expr (aexpr);
+ string_printf (p, NULL, gen_printf_expr_callback,
+ loc, aexpr);
+ ax_simple (aexpr, aop_end);
+ /* The agent expr include expr for arguments, format string, 1 byte
+ * for aop_printf, 1 byte for the number of arguments, 1 byte for
+ * size of format string, 1 byte for blank after format string
+ * and 1 byte for aop_end. */
+ if (aexpr->len > MAX_AGENT_EXPR_LEN)
+ error (_("Expression is too complicated."));
+ do_cleanups (old_chain);
+ }
+ }
+
else if (cmd_cfunc_eq (c, end_actions_pseudocommand))
;

@@ -1430,6 +1461,22 @@ encode_actions_1 (struct command_line *a
encode_actions_1 (action->body_list[0], t, tloc, frame_reg,
frame_offset, stepping_list, NULL);
}
+ else if (cmd_cfunc_eq (cmd, printf_command))
+ {
+ char fbuf[101];
+ struct cleanup *old_chain = NULL;
+
+ aexpr = new_agent_expr (tloc->gdbarch, tloc->address);
+ old_chain = make_cleanup_free_agent_expr (aexpr);
+ string_printf (action_exp, NULL, gen_printf_expr_callback,
+ tloc, aexpr);
+ ax_simple (aexpr, aop_end);
+
+ ax_reqs (aexpr);
+ report_agent_reqs_errors (aexpr);
+ discard_cleanups (old_chain);
+ add_aexpr (collect, aexpr);
+ }
else
error (_("Invalid tracepoint command '%s'"), action->line);
} /* for */