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

Hui Zhu
2011-01-28T05:52:53Z

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

On Thu, Jan 6, 2011 at 14:43, Hui Zhu <teawater@gmail.com> wrote:
> On Thu, Jan 6, 2011 at 04:51, Stan Shebs <stan@codesourcery.com> wrote:
>> On 1/3/11 10:18 PM, Doug Evans wrote:
>>>
>>> [for reference sake]
>>> To me this is a subset of a bigger feature set that is missing:
>>> partitioning of the things that can be accomplished by gdbserver from
>>> the setup that is needed (IOW separate the heavy lifting of parsing
>>> debug info and translating a user query into, for example, an agent
>>> expression (the gdb side) from the processing of that query when the
>>> breakpoint(/tracepoint) is hit (the gdbserver side).
>>> Plus it might be useful to not require a gdb/gdbserver connection to
>>> get things started, e.g., convey the tracepoint info (and anything
>>> else) to gdbserver from a local source.
>>> [I'm using "query" loosely here.  I'm using "gdbserver" loosely too:
>>> anything that looks like gdbserver to gdb will do.]
>>
>> I'm actually working on a contract proposal to do a bunch of work in this
>> area.
>>
>> One of the specific ideas is to introduce a "dynamic printf" that works
>> somewhat like what I think Hui Zhu was wanting; it stops the program at a
>> location, runs the printf in gdbserver, and then continues.
>>
>> Another one of the ideas is to use agent expressions to do target-side
>> conditional breakpoints.  This is especially compelling for many-core
>> targets, where we don't want 100 threads on 100 cores to be trying to get
>> GDB to do 100 conditional expression evaluations all at once.
>>
>> I didn't specifically propose to go beyond that, into general partitioning
>> of command lists between host and target, although it's a very interesting
>> direction.  The idea gets me to thinking about whether we should keep the
>> command list form, or raise it to the level of a real language, or maybe
>> support several - Mentor's EDGE debugger for instance uses a C syntax for
>> its "codelets" (
>> http://www.mentor.com/embedded-software/resources/overview/codelets-15b9eaed-8e4b-43a2-aad5-c189cd7f2d68
>> ) even though they run on the host, and whether the language is C or Python,
>> it seems useful to be able to inject real code bits into the target system.
>>
>> Anyway, if we get the contract (fingers crossed!) then I expect we'll be
>> putting up some proposals for discussion within the next couple of months.
>>
>> I'll comment on the patch in a different message.
>>
>> Stan
>>
>>
>
> That will be great!  Thanks Stan.
>
> Hui
>

Update follow the trunk.

Thanks,
Hui

2011-01-28 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
@@ -2446,6 +2446,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
@@ -319,6 +319,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

@@ -376,6 +384,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 */
};

@@ -401,6 +410,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)
@@ -409,7 +419,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);
@@ -417,15 +439,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);
}
@@ -493,6 +515,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]));

@@ -500,7 +524,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])))
{
@@ -516,7 +540,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;
@@ -534,7 +574,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
@@ -1958,10 +1958,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;
@@ -2294,26 +2297,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++)
@@ -2668,12 +2687,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);

@@ -725,6 +734,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))
;

@@ -1436,6 +1467,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 */