binutils-gdb/gdb/common/format.c
Stan Shebs d3ce09f5bf Add target-side support for dynamic printf.
* NEWS: Mention the additional style.
	* breakpoint.h (struct bp_target_info): New fields tcommands, persist.
	(struct bp_location): New field cmd_bytecode.
	* breakpoint.c: Include format.h.
	(disconnected_dprintf): New global.
	(parse_cmd_to_aexpr): New function.
	(build_target_command_list): New function.
	(insert_bp_location): Call it.
	(remove_breakpoints_pid): Skip dprintf breakpoints.
	(print_one_breakpoint_location): Ditto.
	(dprintf_style_agent): New global.
	(dprintf_style_enums): Add dprintf_style_agent.
	(update_dprintf_command_list): Add agent case.
	(agent_printf_command): New function.
	(_initialize_breakpoint): Add new commands.
	* common/ax.def (printf): New bytecode.
	* ax.h (ax_string): Declare.
	* ax-gdb.h (gen_printf): Declare.
	* ax-gdb.c: Include cli-utils.h, format.h.
	(gen_printf): New function.
	(maint_agent_print_command): New function.
	(_initialize_ax_gdb): Add maint agent-printf command.
	* ax-general.c (ax_string): New function.
	(ax_print): Add printf disassembly.
	* Makefile.in (SFILES): Add format.c
	(COMMON_OBS): Add format.o.
	* common/format.h: New file.
	* common/format.c: New file.
	* printcmd.c: Include format.h.
	(ui_printf): Call parse_format_string.
	* remote.c (remote_state): New field breakpoint_commands.
	(PACKET_BreakpointCommands): New enum.
	(remote_breakpoint_commands_feature): New function.
	(remote_protocol_features): Add new BreakpointCommands entry.
	(remote_can_run_breakpoint_commands): New function.
	(remote_add_target_side_commands): New function.
	(remote_insert_breakpoint): Call it.
	(remote_insert_hw_breakpoint): Ditto.
	(_initialize_remote): Add new packet configuration for
	target-side breakpoint commands.
	* target.h (struct target_ops): New field
	to_can_run_breakpoint_commands.
	(target_can_run_breakpoint_commands): New macro.
	* target.c (update_current_target): Handle
	to_can_run_breakpoint_commands.

	[gdbserver]
	* Makefile.in (WARN_CFLAGS_NO_FORMAT): Define.
	(ax.o): Add it to build rule.
	(ax-ipa.o): Ditto.
	(OBS): Add format.o.
	(IPA_OBS): Add format.o.
	* server.c (handle_query): Claim support for breakpoint commands.
	(process_point_options): Add command case.
	(process_serial_event): Leave running if there are printfs in
	effect.
	* mem-break.h (any_persistent_commands): Declare.
	(add_breakpoint_commands): Declare.
	(gdb_no_commands_at_breakpoint): Declare.
	(run_breakpoint_commands): Declare.
	* mem-break.c (struct point_command_list): New struct.
	(struct breakpoint): New field command_list.
	(any_persistent_commands): New function.
	(add_commands_to_breakpoint): New function.
	(add_breakpoint_commands): New function.
	(gdb_no_commands_at_breakpoint): New function.
	(run_breakpoint_commands): New function.
	* linux-low.c (linux_wait_1): Test for and run breakpoint commands
	locally.
	* ax.c: Include format.h.
	(ax_printf): New function.
	(gdb_eval_agent_expr): Add printf opcode.

	[doc]
	* gdb.texinfo (Dynamic Printf): Mention agent style and
	disconnected dprintf.
	(Maintenance Commands): Describe maint agent-printf.
	(General Query Packets): Mention BreakpointCommands feature.
	(Packets): Document commands extension to Z0 packet.
	* agentexpr.texi (Bytecode Descriptions): Document printf
	bytecode.

	[testsuite]
	* gdb.base/dprintf.exp: Add agent style tests.
2012-07-02 15:29:39 +00:00

401 lines
8.6 KiB
C

/* Parse a printf-style format string.
Copyright (C) 1986-2012 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifdef GDBSERVER
#include "server.h"
#else
#include "defs.h"
#endif
#include <string.h>
#include "format.h"
struct format_piece *
parse_format_string (char **arg)
{
char *s, *f, *string;
char *prev_start;
char *percent_loc;
char *sub_start, *current_substring;
struct format_piece *pieces;
int next_frag;
int max_pieces;
enum argclass this_argclass;
s = *arg;
/* Parse the format-control string and copy it into the string STRING,
processing some kinds of escape sequence. */
f = string = (char *) alloca (strlen (s) + 1);
while (*s != '"' && *s != '\0')
{
int c = *s++;
switch (c)
{
case '\0':
continue;
case '\\':
switch (c = *s++)
{
case '\\':
*f++ = '\\';
break;
case 'a':
*f++ = '\a';
break;
case 'b':
*f++ = '\b';
break;
case 'f':
*f++ = '\f';
break;
case 'n':
*f++ = '\n';
break;
case 'r':
*f++ = '\r';
break;
case 't':
*f++ = '\t';
break;
case 'v':
*f++ = '\v';
break;
case '"':
*f++ = '"';
break;
default:
/* ??? TODO: handle other escape sequences. */
error (_("Unrecognized escape character \\%c in format string."),
c);
}
break;
default:
*f++ = c;
}
}
/* Terminate our escape-processed copy. */
*f++ = '\0';
/* Whether the format string ended with double-quote or zero, we're
done with it; it's up to callers to complain about syntax. */
*arg = s;
/* Need extra space for the '\0's. Doubling the size is sufficient. */
current_substring = xmalloc (strlen (string) * 2 + 1000);
max_pieces = strlen (string) + 2;
pieces = (struct format_piece *)
xmalloc (max_pieces * sizeof (struct format_piece));
next_frag = 0;
/* Now scan the string for %-specs and see what kinds of args they want.
argclass classifies the %-specs so we can give printf-type functions
something of the right size. */
f = string;
prev_start = string;
while (*f)
if (*f++ == '%')
{
int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
int seen_space = 0, seen_plus = 0;
int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
int seen_big_d = 0, seen_double_big_d = 0;
int bad = 0;
/* Skip over "%%", it will become part of a literal piece. */
if (*f == '%')
{
f++;
continue;
}
sub_start = current_substring;
strncpy (current_substring, prev_start, f - 1 - prev_start);
current_substring += f - 1 - prev_start;
*current_substring++ = '\0';
pieces[next_frag].string = sub_start;
pieces[next_frag].argclass = literal_piece;
next_frag++;
percent_loc = f - 1;
/* Check the validity of the format specifier, and work
out what argument it expects. We only accept C89
format strings, with the exception of long long (which
we autoconf for). */
/* The first part of a format specifier is a set of flag
characters. */
while (strchr ("0-+ #", *f))
{
if (*f == '#')
seen_hash = 1;
else if (*f == '0')
seen_zero = 1;
else if (*f == ' ')
seen_space = 1;
else if (*f == '+')
seen_plus = 1;
f++;
}
/* The next part of a format specifier is a width. */
while (strchr ("0123456789", *f))
f++;
/* The next part of a format specifier is a precision. */
if (*f == '.')
{
seen_prec = 1;
f++;
while (strchr ("0123456789", *f))
f++;
}
/* The next part of a format specifier is a length modifier. */
if (*f == 'h')
{
seen_h = 1;
f++;
}
else if (*f == 'l')
{
f++;
lcount++;
if (*f == 'l')
{
f++;
lcount++;
}
}
else if (*f == 'L')
{
seen_big_l = 1;
f++;
}
/* Decimal32 modifier. */
else if (*f == 'H')
{
seen_big_h = 1;
f++;
}
/* Decimal64 and Decimal128 modifiers. */
else if (*f == 'D')
{
f++;
/* Check for a Decimal128. */
if (*f == 'D')
{
f++;
seen_double_big_d = 1;
}
else
seen_big_d = 1;
}
switch (*f)
{
case 'u':
if (seen_hash)
bad = 1;
/* FALLTHROUGH */
case 'o':
case 'x':
case 'X':
if (seen_space || seen_plus)
bad = 1;
/* FALLTHROUGH */
case 'd':
case 'i':
if (lcount == 0)
this_argclass = int_arg;
else if (lcount == 1)
this_argclass = long_arg;
else
this_argclass = long_long_arg;
if (seen_big_l)
bad = 1;
break;
case 'c':
this_argclass = lcount == 0 ? int_arg : wide_char_arg;
if (lcount > 1 || seen_h || seen_big_l)
bad = 1;
if (seen_prec || seen_zero || seen_space || seen_plus)
bad = 1;
break;
case 'p':
this_argclass = ptr_arg;
if (lcount || seen_h || seen_big_l)
bad = 1;
if (seen_prec || seen_zero || seen_space || seen_plus)
bad = 1;
break;
case 's':
this_argclass = lcount == 0 ? string_arg : wide_string_arg;
if (lcount > 1 || seen_h || seen_big_l)
bad = 1;
if (seen_zero || seen_space || seen_plus)
bad = 1;
break;
case 'e':
case 'f':
case 'g':
case 'E':
case 'G':
if (seen_big_h || seen_big_d || seen_double_big_d)
this_argclass = decfloat_arg;
else if (seen_big_l)
this_argclass = long_double_arg;
else
this_argclass = double_arg;
if (lcount || seen_h)
bad = 1;
break;
case '*':
error (_("`*' not supported for precision or width in printf"));
case 'n':
error (_("Format specifier `n' not supported in printf"));
case '\0':
error (_("Incomplete format specifier at end of format string"));
default:
error (_("Unrecognized format specifier '%c' in printf"), *f);
}
if (bad)
error (_("Inappropriate modifiers to "
"format specifier '%c' in printf"),
*f);
f++;
sub_start = current_substring;
if (lcount > 1 && USE_PRINTF_I64)
{
/* Windows' printf does support long long, but not the usual way.
Convert %lld to %I64d. */
int length_before_ll = f - percent_loc - 1 - lcount;
strncpy (current_substring, percent_loc, length_before_ll);
strcpy (current_substring + length_before_ll, "I64");
current_substring[length_before_ll + 3] =
percent_loc[length_before_ll + lcount];
current_substring += length_before_ll + 4;
}
else if (this_argclass == wide_string_arg
|| this_argclass == wide_char_arg)
{
/* Convert %ls or %lc to %s. */
int length_before_ls = f - percent_loc - 2;
strncpy (current_substring, percent_loc, length_before_ls);
strcpy (current_substring + length_before_ls, "s");
current_substring += length_before_ls + 2;
}
else
{
strncpy (current_substring, percent_loc, f - percent_loc);
current_substring += f - percent_loc;
}
*current_substring++ = '\0';
prev_start = f;
pieces[next_frag].string = sub_start;
pieces[next_frag].argclass = this_argclass;
next_frag++;
}
/* Record the remainder of the string. */
sub_start = current_substring;
strncpy (current_substring, prev_start, f - prev_start);
current_substring += f - prev_start;
*current_substring++ = '\0';
pieces[next_frag].string = sub_start;
pieces[next_frag].argclass = literal_piece;
next_frag++;
/* Record an end-of-array marker. */
pieces[next_frag].string = NULL;
pieces[next_frag].argclass = literal_piece;
return pieces;
}
void
free_format_pieces (struct format_piece *pieces)
{
if (!pieces)
return;
/* We happen to know that all the string pieces are in the block
pointed to by the first string piece. */
if (pieces[0].string)
xfree (pieces[0].string);
xfree (pieces);
}
void
free_format_pieces_cleanup (void *ptr)
{
void **location = ptr;
if (location == NULL)
return;
if (*location != NULL)
{
free_format_pieces (*location);
*location = NULL;
}
}