Browse Source
* 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.cygwin-64bit-branch
28 changed files with 1619 additions and 384 deletions
@ -0,0 +1,400 @@ |
|||
/* 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; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,63 @@ |
|||
/* 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/>. */
|
|||
|
|||
#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) |
|||
# define USE_PRINTF_I64 1 |
|||
# define PRINTF_HAS_LONG_LONG |
|||
#else |
|||
# define USE_PRINTF_I64 0 |
|||
#endif |
|||
|
|||
/* The argclass represents the general type of data that goes with a
|
|||
format directive; int_arg for %d, long_arg for %l, and so forth. |
|||
Note that these primarily distinguish types by size and need for |
|||
special handling, so for instance %u and %x are (at present) also |
|||
classed as int_arg. */ |
|||
|
|||
enum argclass |
|||
{ |
|||
literal_piece, |
|||
int_arg, long_arg, long_long_arg, ptr_arg, |
|||
string_arg, wide_string_arg, wide_char_arg, |
|||
double_arg, long_double_arg, decfloat_arg |
|||
}; |
|||
|
|||
/* A format piece is a section of the format string that may include a
|
|||
single print directive somewhere in it, and the associated class |
|||
for the argument. */ |
|||
|
|||
struct format_piece |
|||
{ |
|||
char *string; |
|||
enum argclass argclass; |
|||
}; |
|||
|
|||
/* Return an array of printf fragments found at the given string, and
|
|||
rewrite ARG with a pointer to the end of the format string. */ |
|||
|
|||
extern struct format_piece *parse_format_string (char **arg); |
|||
|
|||
/* Given a pointer to an array of format pieces, free any memory that
|
|||
would have been allocated by parse_format_string. */ |
|||
|
|||
extern void free_format_pieces (struct format_piece *frags); |
|||
|
|||
/* Freeing, cast as a cleanup. */ |
|||
|
|||
extern void free_format_pieces_cleanup (void *); |
|||
Loading…
Reference in new issue