2012-04-27 22:47:57 +02:00
|
|
|
|
/* SystemTap probe support for GDB.
|
|
|
|
|
|
2013-01-01 07:33:28 +01:00
|
|
|
|
Copyright (C) 2012-2013 Free Software Foundation, Inc.
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
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/>. */
|
|
|
|
|
|
|
|
|
|
#include "defs.h"
|
|
|
|
|
#include "stap-probe.h"
|
|
|
|
|
#include "probe.h"
|
|
|
|
|
#include "vec.h"
|
|
|
|
|
#include "ui-out.h"
|
|
|
|
|
#include "objfiles.h"
|
|
|
|
|
#include "arch-utils.h"
|
|
|
|
|
#include "command.h"
|
|
|
|
|
#include "gdbcmd.h"
|
|
|
|
|
#include "filenames.h"
|
|
|
|
|
#include "value.h"
|
|
|
|
|
#include "exceptions.h"
|
|
|
|
|
#include "ax.h"
|
|
|
|
|
#include "ax-gdb.h"
|
|
|
|
|
#include "complaints.h"
|
|
|
|
|
#include "cli/cli-utils.h"
|
|
|
|
|
#include "linespec.h"
|
|
|
|
|
#include "user-regs.h"
|
|
|
|
|
#include "parser-defs.h"
|
|
|
|
|
#include "language.h"
|
|
|
|
|
#include "elf-bfd.h"
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
/* The name of the SystemTap section where we will find information about
|
|
|
|
|
the probes. */
|
|
|
|
|
|
|
|
|
|
#define STAP_BASE_SECTION_NAME ".stapsdt.base"
|
|
|
|
|
|
|
|
|
|
/* Forward declaration. */
|
|
|
|
|
|
|
|
|
|
static const struct probe_ops stap_probe_ops;
|
|
|
|
|
|
|
|
|
|
/* Should we display debug information for the probe's argument expression
|
|
|
|
|
parsing? */
|
|
|
|
|
|
2012-08-02 11:36:40 +02:00
|
|
|
|
static unsigned int stap_expression_debug = 0;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
/* The various possibilities of bitness defined for a probe's argument.
|
|
|
|
|
|
|
|
|
|
The relationship is:
|
|
|
|
|
|
|
|
|
|
- STAP_ARG_BITNESS_UNDEFINED: The user hasn't specified the bitness.
|
|
|
|
|
- STAP_ARG_BITNESS_32BIT_UNSIGNED: argument string starts with `4@'.
|
|
|
|
|
- STAP_ARG_BITNESS_32BIT_SIGNED: argument string starts with `-4@'.
|
|
|
|
|
- STAP_ARG_BITNESS_64BIT_UNSIGNED: argument string starts with `8@'.
|
|
|
|
|
- STAP_ARG_BITNESS_64BIT_SIGNED: argument string starts with `-8@'. */
|
|
|
|
|
|
|
|
|
|
enum stap_arg_bitness
|
|
|
|
|
{
|
|
|
|
|
STAP_ARG_BITNESS_UNDEFINED,
|
|
|
|
|
STAP_ARG_BITNESS_32BIT_UNSIGNED,
|
|
|
|
|
STAP_ARG_BITNESS_32BIT_SIGNED,
|
|
|
|
|
STAP_ARG_BITNESS_64BIT_UNSIGNED,
|
|
|
|
|
STAP_ARG_BITNESS_64BIT_SIGNED,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* The following structure represents a single argument for the probe. */
|
|
|
|
|
|
|
|
|
|
struct stap_probe_arg
|
|
|
|
|
{
|
|
|
|
|
/* The bitness of this argument. */
|
|
|
|
|
enum stap_arg_bitness bitness;
|
|
|
|
|
|
|
|
|
|
/* The corresponding `struct type *' to the bitness. */
|
|
|
|
|
struct type *atype;
|
|
|
|
|
|
|
|
|
|
/* The argument converted to an internal GDB expression. */
|
|
|
|
|
struct expression *aexpr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct stap_probe_arg stap_probe_arg_s;
|
|
|
|
|
DEF_VEC_O (stap_probe_arg_s);
|
|
|
|
|
|
|
|
|
|
struct stap_probe
|
|
|
|
|
{
|
|
|
|
|
/* Generic information about the probe. This shall be the first element
|
|
|
|
|
of this struct, in order to maintain binary compatibility with the
|
|
|
|
|
`struct probe' and be able to fully abstract it. */
|
|
|
|
|
struct probe p;
|
|
|
|
|
|
|
|
|
|
/* If the probe has a semaphore associated, then this is the value of
|
|
|
|
|
it. */
|
|
|
|
|
CORE_ADDR sem_addr;
|
|
|
|
|
|
|
|
|
|
unsigned int args_parsed : 1;
|
|
|
|
|
union
|
|
|
|
|
{
|
|
|
|
|
const char *text;
|
|
|
|
|
|
|
|
|
|
/* Information about each argument. This is an array of `stap_probe_arg',
|
|
|
|
|
with each entry representing one argument. */
|
|
|
|
|
VEC (stap_probe_arg_s) *vec;
|
|
|
|
|
}
|
|
|
|
|
args_u;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* When parsing the arguments, we have to establish different precedences
|
|
|
|
|
for the various kinds of asm operators. This enumeration represents those
|
|
|
|
|
precedences.
|
|
|
|
|
|
|
|
|
|
This logic behind this is available at
|
|
|
|
|
<http://sourceware.org/binutils/docs/as/Infix-Ops.html#Infix-Ops>, or using
|
|
|
|
|
the command "info '(as)Infix Ops'". */
|
|
|
|
|
|
|
|
|
|
enum stap_operand_prec
|
|
|
|
|
{
|
|
|
|
|
/* Lowest precedence, used for non-recognized operands or for the beginning
|
|
|
|
|
of the parsing process. */
|
|
|
|
|
STAP_OPERAND_PREC_NONE = 0,
|
|
|
|
|
|
|
|
|
|
/* Precedence of logical OR. */
|
|
|
|
|
STAP_OPERAND_PREC_LOGICAL_OR,
|
|
|
|
|
|
|
|
|
|
/* Precedence of logical AND. */
|
|
|
|
|
STAP_OPERAND_PREC_LOGICAL_AND,
|
|
|
|
|
|
|
|
|
|
/* Precedence of additive (plus, minus) and comparative (equal, less,
|
|
|
|
|
greater-than, etc) operands. */
|
|
|
|
|
STAP_OPERAND_PREC_ADD_CMP,
|
|
|
|
|
|
|
|
|
|
/* Precedence of bitwise operands (bitwise OR, XOR, bitwise AND,
|
|
|
|
|
logical NOT). */
|
|
|
|
|
STAP_OPERAND_PREC_BITWISE,
|
|
|
|
|
|
|
|
|
|
/* Precedence of multiplicative operands (multiplication, division,
|
|
|
|
|
remainder, left shift and right shift). */
|
|
|
|
|
STAP_OPERAND_PREC_MUL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void stap_parse_argument_1 (struct stap_parse_info *p, int has_lhs,
|
|
|
|
|
enum stap_operand_prec prec);
|
|
|
|
|
|
|
|
|
|
static void stap_parse_argument_conditionally (struct stap_parse_info *p);
|
|
|
|
|
|
|
|
|
|
/* Returns 1 if *S is an operator, zero otherwise. */
|
|
|
|
|
|
2012-05-03 22:04:06 +02:00
|
|
|
|
static int stap_is_operator (const char *op);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
show_stapexpressiondebug (struct ui_file *file, int from_tty,
|
|
|
|
|
struct cmd_list_element *c, const char *value)
|
|
|
|
|
{
|
|
|
|
|
fprintf_filtered (file, _("SystemTap Probe expression debugging is %s.\n"),
|
|
|
|
|
value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the operator precedence level of OP, or STAP_OPERAND_PREC_NONE
|
|
|
|
|
if the operator code was not recognized. */
|
|
|
|
|
|
|
|
|
|
static enum stap_operand_prec
|
|
|
|
|
stap_get_operator_prec (enum exp_opcode op)
|
|
|
|
|
{
|
|
|
|
|
switch (op)
|
|
|
|
|
{
|
|
|
|
|
case BINOP_LOGICAL_OR:
|
|
|
|
|
return STAP_OPERAND_PREC_LOGICAL_OR;
|
|
|
|
|
|
|
|
|
|
case BINOP_LOGICAL_AND:
|
|
|
|
|
return STAP_OPERAND_PREC_LOGICAL_AND;
|
|
|
|
|
|
|
|
|
|
case BINOP_ADD:
|
|
|
|
|
case BINOP_SUB:
|
|
|
|
|
case BINOP_EQUAL:
|
|
|
|
|
case BINOP_NOTEQUAL:
|
|
|
|
|
case BINOP_LESS:
|
|
|
|
|
case BINOP_LEQ:
|
|
|
|
|
case BINOP_GTR:
|
|
|
|
|
case BINOP_GEQ:
|
|
|
|
|
return STAP_OPERAND_PREC_ADD_CMP;
|
|
|
|
|
|
|
|
|
|
case BINOP_BITWISE_IOR:
|
|
|
|
|
case BINOP_BITWISE_AND:
|
|
|
|
|
case BINOP_BITWISE_XOR:
|
|
|
|
|
case UNOP_LOGICAL_NOT:
|
|
|
|
|
return STAP_OPERAND_PREC_BITWISE;
|
|
|
|
|
|
|
|
|
|
case BINOP_MUL:
|
|
|
|
|
case BINOP_DIV:
|
|
|
|
|
case BINOP_REM:
|
|
|
|
|
case BINOP_LSH:
|
|
|
|
|
case BINOP_RSH:
|
|
|
|
|
return STAP_OPERAND_PREC_MUL;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return STAP_OPERAND_PREC_NONE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given S, read the operator in it and fills the OP pointer with its code.
|
|
|
|
|
Return 1 on success, zero if the operator was not recognized. */
|
|
|
|
|
|
2012-05-03 22:04:06 +02:00
|
|
|
|
static enum exp_opcode
|
|
|
|
|
stap_get_opcode (const char **s)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
const char c = **s;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
enum exp_opcode op;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
*s += 1;
|
|
|
|
|
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case '*':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_MUL;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '/':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_DIV;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '%':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_REM;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '<':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_LESS;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (**s == '<')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_LSH;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
else if (**s == '=')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_LEQ;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
else if (**s == '>')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_NOTEQUAL;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '>':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_GTR;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (**s == '>')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_RSH;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
else if (**s == '=')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_GEQ;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '|':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_BITWISE_IOR;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (**s == '|')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_LOGICAL_OR;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '&':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_BITWISE_AND;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (**s == '&')
|
|
|
|
|
{
|
|
|
|
|
*s += 1;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_LOGICAL_AND;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '^':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_BITWISE_XOR;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '!':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = UNOP_LOGICAL_NOT;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '+':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_ADD;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '-':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
op = BINOP_SUB;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '=':
|
2012-05-03 22:04:06 +02:00
|
|
|
|
gdb_assert (**s == '=');
|
|
|
|
|
op = BINOP_EQUAL;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2012-05-03 22:04:06 +02:00
|
|
|
|
internal_error (__FILE__, __LINE__,
|
|
|
|
|
_("Invalid opcode in expression `%s' for SystemTap"
|
|
|
|
|
"probe"), *s);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-03 22:04:06 +02:00
|
|
|
|
return op;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given the bitness of the argument, represented by B, return the
|
|
|
|
|
corresponding `struct type *'. */
|
|
|
|
|
|
|
|
|
|
static struct type *
|
|
|
|
|
stap_get_expected_argument_type (struct gdbarch *gdbarch,
|
|
|
|
|
enum stap_arg_bitness b)
|
|
|
|
|
{
|
|
|
|
|
switch (b)
|
|
|
|
|
{
|
|
|
|
|
case STAP_ARG_BITNESS_UNDEFINED:
|
|
|
|
|
if (gdbarch_addr_bit (gdbarch) == 32)
|
|
|
|
|
return builtin_type (gdbarch)->builtin_uint32;
|
|
|
|
|
else
|
|
|
|
|
return builtin_type (gdbarch)->builtin_uint64;
|
|
|
|
|
|
|
|
|
|
case STAP_ARG_BITNESS_32BIT_SIGNED:
|
|
|
|
|
return builtin_type (gdbarch)->builtin_int32;
|
|
|
|
|
|
|
|
|
|
case STAP_ARG_BITNESS_32BIT_UNSIGNED:
|
|
|
|
|
return builtin_type (gdbarch)->builtin_uint32;
|
|
|
|
|
|
|
|
|
|
case STAP_ARG_BITNESS_64BIT_SIGNED:
|
|
|
|
|
return builtin_type (gdbarch)->builtin_int64;
|
|
|
|
|
|
|
|
|
|
case STAP_ARG_BITNESS_64BIT_UNSIGNED:
|
|
|
|
|
return builtin_type (gdbarch)->builtin_uint64;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
internal_error (__FILE__, __LINE__,
|
|
|
|
|
_("Undefined bitness for probe."));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Function responsible for parsing a register operand according to
|
|
|
|
|
SystemTap parlance. Assuming:
|
|
|
|
|
|
|
|
|
|
RP = register prefix
|
|
|
|
|
RS = register suffix
|
|
|
|
|
RIP = register indirection prefix
|
|
|
|
|
RIS = register indirection suffix
|
|
|
|
|
|
|
|
|
|
Then a register operand can be:
|
|
|
|
|
|
|
|
|
|
[RIP] [RP] REGISTER [RS] [RIS]
|
|
|
|
|
|
|
|
|
|
This function takes care of a register's indirection, displacement and
|
|
|
|
|
direct access. It also takes into consideration the fact that some
|
|
|
|
|
registers are named differently inside and outside GDB, e.g., PPC's
|
|
|
|
|
general-purpose registers are represented by integers in the assembly
|
|
|
|
|
language (e.g., `15' is the 15th general-purpose register), but inside
|
|
|
|
|
GDB they have a prefix (the letter `r') appended. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_parse_register_operand (struct stap_parse_info *p)
|
|
|
|
|
{
|
|
|
|
|
/* Simple flag to indicate whether we have seen a minus signal before
|
|
|
|
|
certain number. */
|
|
|
|
|
int got_minus = 0;
|
|
|
|
|
|
|
|
|
|
/* Flags to indicate whether this register access is being displaced and/or
|
|
|
|
|
indirected. */
|
|
|
|
|
int disp_p = 0, indirect_p = 0;
|
|
|
|
|
struct gdbarch *gdbarch = p->gdbarch;
|
|
|
|
|
|
|
|
|
|
/* Needed to generate the register name as a part of an expression. */
|
|
|
|
|
struct stoken str;
|
|
|
|
|
|
|
|
|
|
/* Variables used to extract the register name from the probe's
|
|
|
|
|
argument. */
|
|
|
|
|
const char *start;
|
|
|
|
|
char *regname;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
/* Prefixes for the parser. */
|
|
|
|
|
const char *reg_prefix = gdbarch_stap_register_prefix (gdbarch);
|
|
|
|
|
const char *reg_ind_prefix
|
|
|
|
|
= gdbarch_stap_register_indirection_prefix (gdbarch);
|
|
|
|
|
const char *gdb_reg_prefix = gdbarch_stap_gdb_register_prefix (gdbarch);
|
|
|
|
|
int reg_prefix_len = reg_prefix ? strlen (reg_prefix) : 0;
|
|
|
|
|
int reg_ind_prefix_len = reg_ind_prefix ? strlen (reg_ind_prefix) : 0;
|
|
|
|
|
int gdb_reg_prefix_len = gdb_reg_prefix ? strlen (gdb_reg_prefix) : 0;
|
|
|
|
|
|
|
|
|
|
/* Suffixes for the parser. */
|
|
|
|
|
const char *reg_suffix = gdbarch_stap_register_suffix (gdbarch);
|
|
|
|
|
const char *reg_ind_suffix
|
|
|
|
|
= gdbarch_stap_register_indirection_suffix (gdbarch);
|
|
|
|
|
const char *gdb_reg_suffix = gdbarch_stap_gdb_register_suffix (gdbarch);
|
|
|
|
|
int reg_suffix_len = reg_suffix ? strlen (reg_suffix) : 0;
|
|
|
|
|
int reg_ind_suffix_len = reg_ind_suffix ? strlen (reg_ind_suffix) : 0;
|
|
|
|
|
int gdb_reg_suffix_len = gdb_reg_suffix ? strlen (gdb_reg_suffix) : 0;
|
|
|
|
|
|
|
|
|
|
/* Checking for a displacement argument. */
|
|
|
|
|
if (*p->arg == '+')
|
|
|
|
|
{
|
|
|
|
|
/* If it's a plus sign, we don't need to do anything, just advance the
|
|
|
|
|
pointer. */
|
|
|
|
|
++p->arg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*p->arg == '-')
|
|
|
|
|
{
|
|
|
|
|
got_minus = 1;
|
|
|
|
|
++p->arg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isdigit (*p->arg))
|
|
|
|
|
{
|
|
|
|
|
/* The value of the displacement. */
|
|
|
|
|
long displacement;
|
2013-03-13 17:45:11 +01:00
|
|
|
|
char *endp;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
disp_p = 1;
|
2013-03-13 17:45:11 +01:00
|
|
|
|
displacement = strtol (p->arg, &endp, 10);
|
|
|
|
|
p->arg = endp;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
/* Generating the expression for the displacement. */
|
|
|
|
|
write_exp_elt_opcode (OP_LONG);
|
|
|
|
|
write_exp_elt_type (builtin_type (gdbarch)->builtin_long);
|
|
|
|
|
write_exp_elt_longcst (displacement);
|
|
|
|
|
write_exp_elt_opcode (OP_LONG);
|
|
|
|
|
if (got_minus)
|
|
|
|
|
write_exp_elt_opcode (UNOP_NEG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Getting rid of register indirection prefix. */
|
|
|
|
|
if (reg_ind_prefix
|
|
|
|
|
&& strncmp (p->arg, reg_ind_prefix, reg_ind_prefix_len) == 0)
|
|
|
|
|
{
|
|
|
|
|
indirect_p = 1;
|
|
|
|
|
p->arg += reg_ind_prefix_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (disp_p && !indirect_p)
|
|
|
|
|
error (_("Invalid register displacement syntax on expression `%s'."),
|
|
|
|
|
p->saved_arg);
|
|
|
|
|
|
|
|
|
|
/* Getting rid of register prefix. */
|
|
|
|
|
if (reg_prefix && strncmp (p->arg, reg_prefix, reg_prefix_len) == 0)
|
|
|
|
|
p->arg += reg_prefix_len;
|
|
|
|
|
|
|
|
|
|
/* Now we should have only the register name. Let's extract it and get
|
|
|
|
|
the associated number. */
|
|
|
|
|
start = p->arg;
|
|
|
|
|
|
|
|
|
|
/* We assume the register name is composed by letters and numbers. */
|
|
|
|
|
while (isalnum (*p->arg))
|
|
|
|
|
++p->arg;
|
|
|
|
|
|
|
|
|
|
len = p->arg - start;
|
|
|
|
|
|
|
|
|
|
regname = alloca (len + gdb_reg_prefix_len + gdb_reg_suffix_len + 1);
|
|
|
|
|
regname[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/* We only add the GDB's register prefix/suffix if we are dealing with
|
|
|
|
|
a numeric register. */
|
|
|
|
|
if (gdb_reg_prefix && isdigit (*start))
|
|
|
|
|
{
|
|
|
|
|
strncpy (regname, gdb_reg_prefix, gdb_reg_prefix_len);
|
|
|
|
|
strncpy (regname + gdb_reg_prefix_len, start, len);
|
|
|
|
|
|
|
|
|
|
if (gdb_reg_suffix)
|
|
|
|
|
strncpy (regname + gdb_reg_prefix_len + len,
|
|
|
|
|
gdb_reg_suffix, gdb_reg_suffix_len);
|
|
|
|
|
|
|
|
|
|
len += gdb_reg_prefix_len + gdb_reg_suffix_len;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
strncpy (regname, start, len);
|
|
|
|
|
|
|
|
|
|
regname[len] = '\0';
|
|
|
|
|
|
|
|
|
|
/* Is this a valid register name? */
|
|
|
|
|
if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1)
|
|
|
|
|
error (_("Invalid register name `%s' on expression `%s'."),
|
|
|
|
|
regname, p->saved_arg);
|
|
|
|
|
|
|
|
|
|
write_exp_elt_opcode (OP_REGISTER);
|
|
|
|
|
str.ptr = regname;
|
|
|
|
|
str.length = len;
|
|
|
|
|
write_exp_string (str);
|
|
|
|
|
write_exp_elt_opcode (OP_REGISTER);
|
|
|
|
|
|
|
|
|
|
if (indirect_p)
|
|
|
|
|
{
|
|
|
|
|
if (disp_p)
|
|
|
|
|
write_exp_elt_opcode (BINOP_ADD);
|
|
|
|
|
|
|
|
|
|
/* Casting to the expected type. */
|
|
|
|
|
write_exp_elt_opcode (UNOP_CAST);
|
|
|
|
|
write_exp_elt_type (lookup_pointer_type (p->arg_type));
|
|
|
|
|
write_exp_elt_opcode (UNOP_CAST);
|
|
|
|
|
|
|
|
|
|
write_exp_elt_opcode (UNOP_IND);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Getting rid of the register name suffix. */
|
|
|
|
|
if (reg_suffix)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp (p->arg, reg_suffix, reg_suffix_len) != 0)
|
|
|
|
|
error (_("Missing register name suffix `%s' on expression `%s'."),
|
|
|
|
|
reg_suffix, p->saved_arg);
|
|
|
|
|
|
|
|
|
|
p->arg += reg_suffix_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Getting rid of the register indirection suffix. */
|
|
|
|
|
if (indirect_p && reg_ind_suffix)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp (p->arg, reg_ind_suffix, reg_ind_suffix_len) != 0)
|
|
|
|
|
error (_("Missing indirection suffix `%s' on expression `%s'."),
|
|
|
|
|
reg_ind_suffix, p->saved_arg);
|
|
|
|
|
|
|
|
|
|
p->arg += reg_ind_suffix_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function is responsible for parsing a single operand.
|
|
|
|
|
|
|
|
|
|
A single operand can be:
|
|
|
|
|
|
|
|
|
|
- an unary operation (e.g., `-5', `~2', or even with subexpressions
|
|
|
|
|
like `-(2 + 1)')
|
|
|
|
|
- a register displacement, which will be treated as a register
|
|
|
|
|
operand (e.g., `-4(%eax)' on x86)
|
|
|
|
|
- a numeric constant, or
|
|
|
|
|
- a register operand (see function `stap_parse_register_operand')
|
|
|
|
|
|
|
|
|
|
The function also calls special-handling functions to deal with
|
|
|
|
|
unrecognized operands, allowing arch-specific parsers to be
|
|
|
|
|
created. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_parse_single_operand (struct stap_parse_info *p)
|
|
|
|
|
{
|
|
|
|
|
struct gdbarch *gdbarch = p->gdbarch;
|
|
|
|
|
|
|
|
|
|
/* Prefixes for the parser. */
|
|
|
|
|
const char *const_prefix = gdbarch_stap_integer_prefix (gdbarch);
|
|
|
|
|
const char *reg_prefix = gdbarch_stap_register_prefix (gdbarch);
|
|
|
|
|
const char *reg_ind_prefix
|
|
|
|
|
= gdbarch_stap_register_indirection_prefix (gdbarch);
|
|
|
|
|
int const_prefix_len = const_prefix ? strlen (const_prefix) : 0;
|
|
|
|
|
int reg_prefix_len = reg_prefix ? strlen (reg_prefix) : 0;
|
|
|
|
|
int reg_ind_prefix_len = reg_ind_prefix ? strlen (reg_ind_prefix) : 0;
|
|
|
|
|
|
|
|
|
|
/* Suffixes for the parser. */
|
|
|
|
|
const char *const_suffix = gdbarch_stap_integer_suffix (gdbarch);
|
|
|
|
|
int const_suffix_len = const_suffix ? strlen (const_suffix) : 0;
|
|
|
|
|
|
|
|
|
|
/* We first try to parse this token as a "special token". */
|
|
|
|
|
if (gdbarch_stap_parse_special_token_p (gdbarch))
|
|
|
|
|
{
|
|
|
|
|
int ret = gdbarch_stap_parse_special_token (gdbarch, p);
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
|
|
|
|
/* If the return value of the above function is not zero,
|
|
|
|
|
it means it successfully parsed the special token.
|
|
|
|
|
|
|
|
|
|
If it is NULL, we try to parse it using our method. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+')
|
|
|
|
|
{
|
|
|
|
|
char c = *p->arg;
|
|
|
|
|
int number;
|
|
|
|
|
|
|
|
|
|
/* We use this variable to do a lookahead. */
|
|
|
|
|
const char *tmp = p->arg;
|
|
|
|
|
|
|
|
|
|
++tmp;
|
|
|
|
|
|
|
|
|
|
/* This is an unary operation. Here is a list of allowed tokens
|
|
|
|
|
here:
|
|
|
|
|
|
|
|
|
|
- numeric literal;
|
|
|
|
|
- number (from register displacement)
|
|
|
|
|
- subexpression (beginning with `(')
|
|
|
|
|
|
|
|
|
|
We handle the register displacement here, and the other cases
|
|
|
|
|
recursively. */
|
|
|
|
|
if (p->inside_paren_p)
|
|
|
|
|
tmp = skip_spaces_const (tmp);
|
|
|
|
|
|
|
|
|
|
if (isdigit (*tmp))
|
2013-03-13 17:45:11 +01:00
|
|
|
|
{
|
|
|
|
|
char *endp;
|
|
|
|
|
|
|
|
|
|
number = strtol (tmp, &endp, 10);
|
|
|
|
|
tmp = endp;
|
|
|
|
|
}
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
if (!reg_ind_prefix
|
|
|
|
|
|| strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) != 0)
|
|
|
|
|
{
|
|
|
|
|
/* This is not a displacement. We skip the operator, and deal
|
|
|
|
|
with it later. */
|
|
|
|
|
++p->arg;
|
|
|
|
|
stap_parse_argument_conditionally (p);
|
|
|
|
|
if (c == '-')
|
|
|
|
|
write_exp_elt_opcode (UNOP_NEG);
|
|
|
|
|
else if (c == '~')
|
|
|
|
|
write_exp_elt_opcode (UNOP_COMPLEMENT);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If we are here, it means it is a displacement. The only
|
|
|
|
|
operations allowed here are `-' and `+'. */
|
|
|
|
|
if (c == '~')
|
|
|
|
|
error (_("Invalid operator `%c' for register displacement "
|
|
|
|
|
"on expression `%s'."), c, p->saved_arg);
|
|
|
|
|
|
|
|
|
|
stap_parse_register_operand (p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (isdigit (*p->arg))
|
|
|
|
|
{
|
|
|
|
|
/* A temporary variable, needed for lookahead. */
|
|
|
|
|
const char *tmp = p->arg;
|
2013-03-13 17:45:11 +01:00
|
|
|
|
char *endp;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
long number;
|
|
|
|
|
|
|
|
|
|
/* We can be dealing with a numeric constant (if `const_prefix' is
|
|
|
|
|
NULL), or with a register displacement. */
|
2013-03-13 17:45:11 +01:00
|
|
|
|
number = strtol (tmp, &endp, 10);
|
|
|
|
|
tmp = endp;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
if (p->inside_paren_p)
|
|
|
|
|
tmp = skip_spaces_const (tmp);
|
|
|
|
|
if (!const_prefix && reg_ind_prefix
|
|
|
|
|
&& strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) != 0)
|
|
|
|
|
{
|
|
|
|
|
/* We are dealing with a numeric constant. */
|
|
|
|
|
write_exp_elt_opcode (OP_LONG);
|
|
|
|
|
write_exp_elt_type (builtin_type (gdbarch)->builtin_long);
|
|
|
|
|
write_exp_elt_longcst (number);
|
|
|
|
|
write_exp_elt_opcode (OP_LONG);
|
|
|
|
|
|
|
|
|
|
p->arg = tmp;
|
|
|
|
|
|
|
|
|
|
if (const_suffix)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp (p->arg, const_suffix, const_suffix_len) == 0)
|
|
|
|
|
p->arg += const_suffix_len;
|
|
|
|
|
else
|
|
|
|
|
error (_("Invalid constant suffix on expression `%s'."),
|
|
|
|
|
p->saved_arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (reg_ind_prefix
|
|
|
|
|
&& strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) == 0)
|
|
|
|
|
stap_parse_register_operand (p);
|
|
|
|
|
else
|
|
|
|
|
error (_("Unknown numeric token on expression `%s'."),
|
|
|
|
|
p->saved_arg);
|
|
|
|
|
}
|
|
|
|
|
else if (const_prefix
|
|
|
|
|
&& strncmp (p->arg, const_prefix, const_prefix_len) == 0)
|
|
|
|
|
{
|
|
|
|
|
/* We are dealing with a numeric constant. */
|
|
|
|
|
long number;
|
2013-03-13 17:45:11 +01:00
|
|
|
|
char *endp;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
p->arg += const_prefix_len;
|
2013-03-13 17:45:11 +01:00
|
|
|
|
number = strtol (p->arg, &endp, 10);
|
|
|
|
|
p->arg = endp;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
write_exp_elt_opcode (OP_LONG);
|
|
|
|
|
write_exp_elt_type (builtin_type (gdbarch)->builtin_long);
|
|
|
|
|
write_exp_elt_longcst (number);
|
|
|
|
|
write_exp_elt_opcode (OP_LONG);
|
|
|
|
|
|
|
|
|
|
if (const_suffix)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp (p->arg, const_suffix, const_suffix_len) == 0)
|
|
|
|
|
p->arg += const_suffix_len;
|
|
|
|
|
else
|
|
|
|
|
error (_("Invalid constant suffix on expression `%s'."),
|
|
|
|
|
p->saved_arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ((reg_prefix
|
|
|
|
|
&& strncmp (p->arg, reg_prefix, reg_prefix_len) == 0)
|
|
|
|
|
|| (reg_ind_prefix
|
|
|
|
|
&& strncmp (p->arg, reg_ind_prefix, reg_ind_prefix_len) == 0))
|
|
|
|
|
stap_parse_register_operand (p);
|
|
|
|
|
else
|
|
|
|
|
error (_("Operator `%c' not recognized on expression `%s'."),
|
|
|
|
|
*p->arg, p->saved_arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function parses an argument conditionally, based on single or
|
|
|
|
|
non-single operands. A non-single operand would be a parenthesized
|
|
|
|
|
expression (e.g., `(2 + 1)'), and a single operand is anything that
|
|
|
|
|
starts with `-', `~', `+' (i.e., unary operators), a digit, or
|
|
|
|
|
something recognized by `gdbarch_stap_is_single_operand'. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_parse_argument_conditionally (struct stap_parse_info *p)
|
|
|
|
|
{
|
|
|
|
|
if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+' /* Unary. */
|
|
|
|
|
|| isdigit (*p->arg)
|
|
|
|
|
|| gdbarch_stap_is_single_operand (p->gdbarch, p->arg))
|
|
|
|
|
stap_parse_single_operand (p);
|
|
|
|
|
else if (*p->arg == '(')
|
|
|
|
|
{
|
|
|
|
|
/* We are dealing with a parenthesized operand. It means we
|
|
|
|
|
have to parse it as it was a separate expression, without
|
|
|
|
|
left-side or precedence. */
|
|
|
|
|
++p->arg;
|
|
|
|
|
p->arg = skip_spaces_const (p->arg);
|
|
|
|
|
++p->inside_paren_p;
|
|
|
|
|
|
|
|
|
|
stap_parse_argument_1 (p, 0, STAP_OPERAND_PREC_NONE);
|
|
|
|
|
|
|
|
|
|
--p->inside_paren_p;
|
|
|
|
|
if (*p->arg != ')')
|
|
|
|
|
error (_("Missign close-paren on expression `%s'."),
|
|
|
|
|
p->saved_arg);
|
|
|
|
|
|
|
|
|
|
++p->arg;
|
|
|
|
|
if (p->inside_paren_p)
|
|
|
|
|
p->arg = skip_spaces_const (p->arg);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
error (_("Cannot parse expression `%s'."), p->saved_arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Helper function for `stap_parse_argument'. Please, see its comments to
|
|
|
|
|
better understand what this function does. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_parse_argument_1 (struct stap_parse_info *p, int has_lhs,
|
|
|
|
|
enum stap_operand_prec prec)
|
|
|
|
|
{
|
|
|
|
|
/* This is an operator-precedence parser.
|
|
|
|
|
|
|
|
|
|
We work with left- and right-sides of expressions, and
|
|
|
|
|
parse them depending on the precedence of the operators
|
|
|
|
|
we find. */
|
|
|
|
|
|
|
|
|
|
if (p->inside_paren_p)
|
|
|
|
|
p->arg = skip_spaces_const (p->arg);
|
|
|
|
|
|
|
|
|
|
if (!has_lhs)
|
|
|
|
|
{
|
|
|
|
|
/* We were called without a left-side, either because this is the
|
|
|
|
|
first call, or because we were called to parse a parenthesized
|
|
|
|
|
expression. It doesn't really matter; we have to parse the
|
|
|
|
|
left-side in order to continue the process. */
|
|
|
|
|
stap_parse_argument_conditionally (p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start to parse the right-side, and to "join" left and right sides
|
|
|
|
|
depending on the operation specified.
|
|
|
|
|
|
|
|
|
|
This loop shall continue until we run out of characters in the input,
|
|
|
|
|
or until we find a close-parenthesis, which means that we've reached
|
|
|
|
|
the end of a sub-expression. */
|
|
|
|
|
while (p->arg && *p->arg && *p->arg != ')' && !isspace (*p->arg))
|
|
|
|
|
{
|
|
|
|
|
const char *tmp_exp_buf;
|
|
|
|
|
enum exp_opcode opcode;
|
|
|
|
|
enum stap_operand_prec cur_prec;
|
|
|
|
|
|
2012-05-03 22:04:06 +02:00
|
|
|
|
if (!stap_is_operator (p->arg))
|
2012-04-27 22:47:57 +02:00
|
|
|
|
error (_("Invalid operator `%c' on expression `%s'."), *p->arg,
|
|
|
|
|
p->saved_arg);
|
|
|
|
|
|
|
|
|
|
/* We have to save the current value of the expression buffer because
|
|
|
|
|
the `stap_get_opcode' modifies it in order to get the current
|
|
|
|
|
operator. If this operator's precedence is lower than PREC, we
|
|
|
|
|
should return and not advance the expression buffer pointer. */
|
|
|
|
|
tmp_exp_buf = p->arg;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
opcode = stap_get_opcode (&tmp_exp_buf);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
cur_prec = stap_get_operator_prec (opcode);
|
|
|
|
|
if (cur_prec < prec)
|
|
|
|
|
{
|
|
|
|
|
/* If the precedence of the operator that we are seeing now is
|
|
|
|
|
lower than the precedence of the first operator seen before
|
|
|
|
|
this parsing process began, it means we should stop parsing
|
|
|
|
|
and return. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p->arg = tmp_exp_buf;
|
|
|
|
|
if (p->inside_paren_p)
|
|
|
|
|
p->arg = skip_spaces_const (p->arg);
|
|
|
|
|
|
|
|
|
|
/* Parse the right-side of the expression. */
|
|
|
|
|
stap_parse_argument_conditionally (p);
|
|
|
|
|
|
|
|
|
|
/* While we still have operators, try to parse another
|
|
|
|
|
right-side, but using the current right-side as a left-side. */
|
2012-05-03 22:04:06 +02:00
|
|
|
|
while (*p->arg && stap_is_operator (p->arg))
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
enum exp_opcode lookahead_opcode;
|
|
|
|
|
enum stap_operand_prec lookahead_prec;
|
|
|
|
|
|
|
|
|
|
/* Saving the current expression buffer position. The explanation
|
|
|
|
|
is the same as above. */
|
|
|
|
|
tmp_exp_buf = p->arg;
|
2012-05-03 22:04:06 +02:00
|
|
|
|
lookahead_opcode = stap_get_opcode (&tmp_exp_buf);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
lookahead_prec = stap_get_operator_prec (lookahead_opcode);
|
|
|
|
|
|
|
|
|
|
if (lookahead_prec <= prec)
|
|
|
|
|
{
|
|
|
|
|
/* If we are dealing with an operator whose precedence is lower
|
|
|
|
|
than the first one, just abandon the attempt. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse the right-side of the expression, but since we already
|
|
|
|
|
have a left-side at this point, set `has_lhs' to 1. */
|
|
|
|
|
stap_parse_argument_1 (p, 1, lookahead_prec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_exp_elt_opcode (opcode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse a probe's argument.
|
|
|
|
|
|
|
|
|
|
Assuming that:
|
|
|
|
|
|
|
|
|
|
LP = literal integer prefix
|
|
|
|
|
LS = literal integer suffix
|
|
|
|
|
|
|
|
|
|
RP = register prefix
|
|
|
|
|
RS = register suffix
|
|
|
|
|
|
|
|
|
|
RIP = register indirection prefix
|
|
|
|
|
RIS = register indirection suffix
|
|
|
|
|
|
|
|
|
|
This routine assumes that arguments' tokens are of the form:
|
|
|
|
|
|
|
|
|
|
- [LP] NUMBER [LS]
|
|
|
|
|
- [RP] REGISTER [RS]
|
|
|
|
|
- [RIP] [RP] REGISTER [RS] [RIS]
|
|
|
|
|
- If we find a number without LP, we try to parse it as a literal integer
|
|
|
|
|
constant (if LP == NULL), or as a register displacement.
|
|
|
|
|
- We count parenthesis, and only skip whitespaces if we are inside them.
|
|
|
|
|
- If we find an operator, we skip it.
|
|
|
|
|
|
|
|
|
|
This function can also call a special function that will try to match
|
|
|
|
|
unknown tokens. It will return 1 if the argument has been parsed
|
|
|
|
|
successfully, or zero otherwise. */
|
|
|
|
|
|
|
|
|
|
static struct expression *
|
|
|
|
|
stap_parse_argument (const char **arg, struct type *atype,
|
|
|
|
|
struct gdbarch *gdbarch)
|
|
|
|
|
{
|
|
|
|
|
struct stap_parse_info p;
|
|
|
|
|
struct cleanup *back_to;
|
|
|
|
|
|
|
|
|
|
/* We need to initialize the expression buffer, in order to begin
|
|
|
|
|
our parsing efforts. The language here does not matter, since we
|
|
|
|
|
are using our own parser. */
|
|
|
|
|
initialize_expout (10, current_language, gdbarch);
|
|
|
|
|
back_to = make_cleanup (free_current_contents, &expout);
|
|
|
|
|
|
|
|
|
|
p.saved_arg = *arg;
|
|
|
|
|
p.arg = *arg;
|
|
|
|
|
p.arg_type = atype;
|
|
|
|
|
p.gdbarch = gdbarch;
|
|
|
|
|
p.inside_paren_p = 0;
|
|
|
|
|
|
|
|
|
|
stap_parse_argument_1 (&p, 0, STAP_OPERAND_PREC_NONE);
|
|
|
|
|
|
|
|
|
|
discard_cleanups (back_to);
|
|
|
|
|
|
|
|
|
|
gdb_assert (p.inside_paren_p == 0);
|
|
|
|
|
|
|
|
|
|
/* Casting the final expression to the appropriate type. */
|
|
|
|
|
write_exp_elt_opcode (UNOP_CAST);
|
|
|
|
|
write_exp_elt_type (atype);
|
|
|
|
|
write_exp_elt_opcode (UNOP_CAST);
|
|
|
|
|
|
|
|
|
|
reallocate_expout ();
|
|
|
|
|
|
|
|
|
|
p.arg = skip_spaces_const (p.arg);
|
|
|
|
|
*arg = p.arg;
|
|
|
|
|
|
|
|
|
|
return expout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Function which parses an argument string from PROBE, correctly splitting
|
|
|
|
|
the arguments and storing their information in properly ways.
|
|
|
|
|
|
|
|
|
|
Consider the following argument string (x86 syntax):
|
|
|
|
|
|
|
|
|
|
`4@%eax 4@$10'
|
|
|
|
|
|
|
|
|
|
We have two arguments, `%eax' and `$10', both with 32-bit unsigned bitness.
|
|
|
|
|
This function basically handles them, properly filling some structures with
|
|
|
|
|
this information. */
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-18 18:12:17 +02:00
|
|
|
|
stap_parse_probe_arguments (struct stap_probe *probe)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
const char *cur;
|
2012-07-18 18:12:17 +02:00
|
|
|
|
struct gdbarch *gdbarch = get_objfile_arch (probe->p.objfile);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
gdb_assert (!probe->args_parsed);
|
|
|
|
|
cur = probe->args_u.text;
|
|
|
|
|
probe->args_parsed = 1;
|
|
|
|
|
probe->args_u.vec = NULL;
|
|
|
|
|
|
|
|
|
|
if (!cur || !*cur || *cur == ':')
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
while (*cur)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe_arg arg;
|
|
|
|
|
enum stap_arg_bitness b;
|
|
|
|
|
int got_minus = 0;
|
|
|
|
|
struct expression *expr;
|
|
|
|
|
|
|
|
|
|
memset (&arg, 0, sizeof (arg));
|
|
|
|
|
|
|
|
|
|
/* We expect to find something like:
|
|
|
|
|
|
|
|
|
|
N@OP
|
|
|
|
|
|
|
|
|
|
Where `N' can be [+,-][4,8]. This is not mandatory, so
|
|
|
|
|
we check it here. If we don't find it, go to the next
|
|
|
|
|
state. */
|
|
|
|
|
if ((*cur == '-' && cur[1] && cur[2] != '@')
|
|
|
|
|
&& cur[1] != '@')
|
|
|
|
|
arg.bitness = STAP_ARG_BITNESS_UNDEFINED;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (*cur == '-')
|
|
|
|
|
{
|
|
|
|
|
/* Discard the `-'. */
|
|
|
|
|
++cur;
|
|
|
|
|
got_minus = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*cur == '4')
|
|
|
|
|
b = (got_minus ? STAP_ARG_BITNESS_32BIT_SIGNED
|
|
|
|
|
: STAP_ARG_BITNESS_32BIT_UNSIGNED);
|
|
|
|
|
else if (*cur == '8')
|
|
|
|
|
b = (got_minus ? STAP_ARG_BITNESS_64BIT_SIGNED
|
|
|
|
|
: STAP_ARG_BITNESS_64BIT_UNSIGNED);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* We have an error, because we don't expect anything
|
|
|
|
|
except 4 and 8. */
|
|
|
|
|
complaint (&symfile_complaints,
|
|
|
|
|
_("unrecognized bitness `%c' for probe `%s'"),
|
|
|
|
|
*cur, probe->p.name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arg.bitness = b;
|
|
|
|
|
arg.atype = stap_get_expected_argument_type (gdbarch, b);
|
|
|
|
|
|
|
|
|
|
/* Discard the number and the `@' sign. */
|
|
|
|
|
cur += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expr = stap_parse_argument (&cur, arg.atype, gdbarch);
|
|
|
|
|
|
|
|
|
|
if (stap_expression_debug)
|
|
|
|
|
dump_raw_expression (expr, gdb_stdlog,
|
|
|
|
|
"before conversion to prefix form");
|
|
|
|
|
|
|
|
|
|
prefixify_expression (expr);
|
|
|
|
|
|
|
|
|
|
if (stap_expression_debug)
|
|
|
|
|
dump_prefix_expression (expr, gdb_stdlog);
|
|
|
|
|
|
|
|
|
|
arg.aexpr = expr;
|
|
|
|
|
|
|
|
|
|
/* Start it over again. */
|
|
|
|
|
cur = skip_spaces_const (cur);
|
|
|
|
|
|
|
|
|
|
VEC_safe_push (stap_probe_arg_s, probe->args_u.vec, &arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given PROBE, returns the number of arguments present in that probe's
|
|
|
|
|
argument string. */
|
|
|
|
|
|
|
|
|
|
static unsigned
|
2012-07-18 18:12:17 +02:00
|
|
|
|
stap_get_probe_argument_count (struct probe *probe_generic)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
struct stap_probe *probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
|
|
|
|
if (!probe->args_parsed)
|
2013-07-24 21:50:32 +02:00
|
|
|
|
{
|
|
|
|
|
if (probe_generic->pops->can_evaluate_probe_arguments (probe_generic))
|
|
|
|
|
stap_parse_probe_arguments (probe);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
static int have_warned_stap_incomplete = 0;
|
|
|
|
|
|
|
|
|
|
if (!have_warned_stap_incomplete)
|
|
|
|
|
{
|
|
|
|
|
warning (_(
|
|
|
|
|
"The SystemTap SDT probe support is not fully implemented on this target;\n"
|
|
|
|
|
"you will not be able to inspect the arguments of the probes.\n"
|
|
|
|
|
"Please report a bug against GDB requesting a port to this target."));
|
|
|
|
|
have_warned_stap_incomplete = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Marking the arguments as "already parsed". */
|
|
|
|
|
probe->args_u.vec = NULL;
|
|
|
|
|
probe->args_parsed = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
gdb_assert (probe->args_parsed);
|
|
|
|
|
return VEC_length (stap_probe_arg_s, probe->args_u.vec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 1 if OP is a valid operator inside a probe argument, or zero
|
|
|
|
|
otherwise. */
|
|
|
|
|
|
|
|
|
|
static int
|
2012-05-03 22:04:06 +02:00
|
|
|
|
stap_is_operator (const char *op)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
2012-05-03 22:04:06 +02:00
|
|
|
|
int ret = 1;
|
|
|
|
|
|
|
|
|
|
switch (*op)
|
|
|
|
|
{
|
|
|
|
|
case '*':
|
|
|
|
|
case '/':
|
|
|
|
|
case '%':
|
|
|
|
|
case '^':
|
|
|
|
|
case '!':
|
|
|
|
|
case '+':
|
|
|
|
|
case '-':
|
|
|
|
|
case '<':
|
|
|
|
|
case '>':
|
|
|
|
|
case '|':
|
|
|
|
|
case '&':
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '=':
|
|
|
|
|
if (op[1] != '=')
|
|
|
|
|
ret = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
/* We didn't find any operator. */
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct stap_probe_arg *
|
2012-07-18 18:12:17 +02:00
|
|
|
|
stap_get_arg (struct stap_probe *probe, unsigned n)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
if (!probe->args_parsed)
|
2012-07-18 18:12:17 +02:00
|
|
|
|
stap_parse_probe_arguments (probe);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
return VEC_index (stap_probe_arg_s, probe->args_u.vec, n);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-24 21:50:32 +02:00
|
|
|
|
/* Implement the `can_evaluate_probe_arguments' method of probe_ops. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stap_can_evaluate_probe_arguments (struct probe *probe_generic)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe *stap_probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
struct gdbarch *gdbarch = stap_probe->p.objfile->gdbarch;
|
|
|
|
|
|
|
|
|
|
/* For SystemTap probes, we have to guarantee that the method
|
|
|
|
|
stap_is_single_operand is defined on gdbarch. If it is not, then it
|
|
|
|
|
means that argument evaluation is not implemented on this target. */
|
|
|
|
|
return gdbarch_stap_is_single_operand_p (gdbarch);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-27 22:47:57 +02:00
|
|
|
|
/* Evaluate the probe's argument N (indexed from 0), returning a value
|
|
|
|
|
corresponding to it. Assertion is thrown if N does not exist. */
|
|
|
|
|
|
|
|
|
|
static struct value *
|
2012-07-18 18:12:17 +02:00
|
|
|
|
stap_evaluate_probe_argument (struct probe *probe_generic, unsigned n)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
struct stap_probe *stap_probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
struct stap_probe_arg *arg;
|
|
|
|
|
int pos = 0;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
arg = stap_get_arg (stap_probe, n);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
return evaluate_subexp_standard (arg->atype, arg->aexpr, &pos, EVAL_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compile the probe's argument N (indexed from 0) to agent expression.
|
|
|
|
|
Assertion is thrown if N does not exist. */
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-18 18:12:17 +02:00
|
|
|
|
stap_compile_to_ax (struct probe *probe_generic, struct agent_expr *expr,
|
|
|
|
|
struct axs_value *value, unsigned n)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
{
|
|
|
|
|
struct stap_probe *stap_probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
struct stap_probe_arg *arg;
|
|
|
|
|
union exp_element *pc;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
arg = stap_get_arg (stap_probe, n);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
pc = arg->aexpr->elts;
|
|
|
|
|
gen_expr (arg->aexpr, &pc, expr, value);
|
|
|
|
|
|
|
|
|
|
require_rvalue (expr, value);
|
|
|
|
|
value->type = arg->atype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Destroy (free) the data related to PROBE. PROBE memory itself is not feed
|
|
|
|
|
as it is allocated from OBJFILE_OBSTACK. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_probe_destroy (struct probe *probe_generic)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe *probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
|
|
|
|
if (probe->args_parsed)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe_arg *arg;
|
|
|
|
|
int ix;
|
|
|
|
|
|
|
|
|
|
for (ix = 0; VEC_iterate (stap_probe_arg_s, probe->args_u.vec, ix, arg);
|
|
|
|
|
++ix)
|
|
|
|
|
xfree (arg->aexpr);
|
|
|
|
|
VEC_free (stap_probe_arg_s, probe->args_u.vec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This is called to compute the value of one of the $_probe_arg*
|
|
|
|
|
convenience variables. */
|
|
|
|
|
|
|
|
|
|
static struct value *
|
|
|
|
|
compute_probe_arg (struct gdbarch *arch, struct internalvar *ivar,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
|
|
|
|
struct frame_info *frame = get_selected_frame (_("No frame selected"));
|
|
|
|
|
CORE_ADDR pc = get_frame_pc (frame);
|
|
|
|
|
int sel = (int) (uintptr_t) data;
|
|
|
|
|
struct probe *pc_probe;
|
2012-07-18 18:12:17 +02:00
|
|
|
|
const struct sym_probe_fns *pc_probe_fns;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
unsigned n_args;
|
|
|
|
|
|
|
|
|
|
/* SEL == -1 means "_probe_argc". */
|
|
|
|
|
gdb_assert (sel >= -1);
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
pc_probe = find_probe_by_pc (pc);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (pc_probe == NULL)
|
|
|
|
|
error (_("No SystemTap probe at PC %s"), core_addr_to_string (pc));
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
gdb_assert (pc_probe->objfile != NULL);
|
|
|
|
|
gdb_assert (pc_probe->objfile->sf != NULL);
|
|
|
|
|
gdb_assert (pc_probe->objfile->sf->sym_probe_fns != NULL);
|
|
|
|
|
|
|
|
|
|
pc_probe_fns = pc_probe->objfile->sf->sym_probe_fns;
|
|
|
|
|
|
|
|
|
|
n_args = pc_probe_fns->sym_get_probe_argument_count (pc_probe);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (sel == -1)
|
|
|
|
|
return value_from_longest (builtin_type (arch)->builtin_int, n_args);
|
|
|
|
|
|
|
|
|
|
if (sel >= n_args)
|
|
|
|
|
error (_("Invalid probe argument %d -- probe has %u arguments available"),
|
|
|
|
|
sel, n_args);
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
return pc_probe_fns->sym_evaluate_probe_argument (pc_probe, sel);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is called to compile one of the $_probe_arg* convenience
|
|
|
|
|
variables into an agent expression. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
compile_probe_arg (struct internalvar *ivar, struct agent_expr *expr,
|
|
|
|
|
struct axs_value *value, void *data)
|
|
|
|
|
{
|
|
|
|
|
CORE_ADDR pc = expr->scope;
|
|
|
|
|
int sel = (int) (uintptr_t) data;
|
|
|
|
|
struct probe *pc_probe;
|
2012-07-18 18:12:17 +02:00
|
|
|
|
const struct sym_probe_fns *pc_probe_fns;
|
2012-07-18 18:20:43 +02:00
|
|
|
|
int n_args;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
/* SEL == -1 means "_probe_argc". */
|
|
|
|
|
gdb_assert (sel >= -1);
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
pc_probe = find_probe_by_pc (pc);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (pc_probe == NULL)
|
|
|
|
|
error (_("No SystemTap probe at PC %s"), core_addr_to_string (pc));
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
gdb_assert (pc_probe->objfile != NULL);
|
|
|
|
|
gdb_assert (pc_probe->objfile->sf != NULL);
|
|
|
|
|
gdb_assert (pc_probe->objfile->sf->sym_probe_fns != NULL);
|
|
|
|
|
|
|
|
|
|
pc_probe_fns = pc_probe->objfile->sf->sym_probe_fns;
|
|
|
|
|
|
2012-07-18 18:20:43 +02:00
|
|
|
|
n_args = pc_probe_fns->sym_get_probe_argument_count (pc_probe);
|
2012-07-18 18:12:17 +02:00
|
|
|
|
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (sel == -1)
|
|
|
|
|
{
|
|
|
|
|
value->kind = axs_rvalue;
|
|
|
|
|
value->type = builtin_type (expr->gdbarch)->builtin_int;
|
2012-07-18 18:20:43 +02:00
|
|
|
|
ax_const_l (expr, n_args);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gdb_assert (sel >= 0);
|
2012-07-18 18:20:43 +02:00
|
|
|
|
if (sel >= n_args)
|
2012-04-27 22:47:57 +02:00
|
|
|
|
error (_("Invalid probe argument %d -- probe has %d arguments available"),
|
2012-07-18 18:20:43 +02:00
|
|
|
|
sel, n_args);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
pc_probe_fns->sym_compile_to_ax (pc_probe, expr, value, sel);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Set or clear a SystemTap semaphore. ADDRESS is the semaphore's
|
|
|
|
|
address. SET is zero if the semaphore should be cleared, or one
|
|
|
|
|
if it should be set. This is a helper function for `stap_semaphore_down'
|
|
|
|
|
and `stap_semaphore_up'. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_modify_semaphore (CORE_ADDR address, int set, struct gdbarch *gdbarch)
|
|
|
|
|
{
|
|
|
|
|
gdb_byte bytes[sizeof (LONGEST)];
|
|
|
|
|
/* The ABI specifies "unsigned short". */
|
|
|
|
|
struct type *type = builtin_type (gdbarch)->builtin_unsigned_short;
|
|
|
|
|
ULONGEST value;
|
|
|
|
|
|
|
|
|
|
if (address == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Swallow errors. */
|
|
|
|
|
if (target_read_memory (address, bytes, TYPE_LENGTH (type)) != 0)
|
|
|
|
|
{
|
|
|
|
|
warning (_("Could not read the value of a SystemTap semaphore."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = extract_unsigned_integer (bytes, TYPE_LENGTH (type),
|
|
|
|
|
gdbarch_byte_order (gdbarch));
|
|
|
|
|
/* Note that we explicitly don't worry about overflow or
|
|
|
|
|
underflow. */
|
|
|
|
|
if (set)
|
|
|
|
|
++value;
|
|
|
|
|
else
|
|
|
|
|
--value;
|
|
|
|
|
|
|
|
|
|
store_unsigned_integer (bytes, TYPE_LENGTH (type),
|
|
|
|
|
gdbarch_byte_order (gdbarch), value);
|
|
|
|
|
|
|
|
|
|
if (target_write_memory (address, bytes, TYPE_LENGTH (type)) != 0)
|
|
|
|
|
warning (_("Could not write the value of a SystemTap semaphore."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set a SystemTap semaphore. SEM is the semaphore's address. Semaphores
|
|
|
|
|
act as reference counters, so calls to this function must be paired with
|
|
|
|
|
calls to `stap_semaphore_down'.
|
|
|
|
|
|
|
|
|
|
This function and `stap_semaphore_down' race with another tool changing
|
|
|
|
|
the probes, but that is too rare to care. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_set_semaphore (struct probe *probe_generic, struct gdbarch *gdbarch)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe *probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
|
|
|
|
stap_modify_semaphore (probe->sem_addr, 1, gdbarch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clear a SystemTap semaphore. SEM is the semaphore's address. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_clear_semaphore (struct probe *probe_generic, struct gdbarch *gdbarch)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe *probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
|
|
|
|
stap_modify_semaphore (probe->sem_addr, 0, gdbarch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Implementation of `$_probe_arg*' set of variables. */
|
|
|
|
|
|
|
|
|
|
static const struct internalvar_funcs probe_funcs =
|
|
|
|
|
{
|
|
|
|
|
compute_probe_arg,
|
|
|
|
|
compile_probe_arg,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Helper function that parses the information contained in a
|
|
|
|
|
SystemTap's probe. Basically, the information consists in:
|
|
|
|
|
|
|
|
|
|
- Probe's PC address;
|
|
|
|
|
- Link-time section address of `.stapsdt.base' section;
|
|
|
|
|
- Link-time address of the semaphore variable, or ZERO if the
|
|
|
|
|
probe doesn't have an associated semaphore;
|
|
|
|
|
- Probe's provider name;
|
|
|
|
|
- Probe's name;
|
|
|
|
|
- Probe's argument format
|
|
|
|
|
|
|
|
|
|
This function returns 1 if the handling was successful, and zero
|
|
|
|
|
otherwise. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_stap_probe (struct objfile *objfile, struct sdt_note *el,
|
|
|
|
|
VEC (probe_p) **probesp, CORE_ADDR base)
|
|
|
|
|
{
|
|
|
|
|
bfd *abfd = objfile->obfd;
|
|
|
|
|
int size = bfd_get_arch_size (abfd) / 8;
|
|
|
|
|
struct gdbarch *gdbarch = get_objfile_arch (objfile);
|
|
|
|
|
struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
|
|
|
|
|
CORE_ADDR base_ref;
|
|
|
|
|
const char *probe_args = NULL;
|
|
|
|
|
struct stap_probe *ret;
|
|
|
|
|
|
|
|
|
|
ret = obstack_alloc (&objfile->objfile_obstack, sizeof (*ret));
|
|
|
|
|
ret->p.pops = &stap_probe_ops;
|
2012-07-18 18:12:17 +02:00
|
|
|
|
ret->p.objfile = objfile;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
/* Provider and the name of the probe. */
|
2013-03-07 19:45:51 +01:00
|
|
|
|
ret->p.provider = (char *) &el->data[3 * size];
|
2012-04-27 22:47:57 +02:00
|
|
|
|
ret->p.name = memchr (ret->p.provider, '\0',
|
|
|
|
|
(char *) el->data + el->size - ret->p.provider);
|
|
|
|
|
/* Making sure there is a name. */
|
|
|
|
|
if (!ret->p.name)
|
|
|
|
|
{
|
|
|
|
|
complaint (&symfile_complaints, _("corrupt probe name when "
|
|
|
|
|
"reading `%s'"), objfile->name);
|
|
|
|
|
|
|
|
|
|
/* There is no way to use a probe without a name or a provider, so
|
|
|
|
|
returning zero here makes sense. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
++ret->p.name;
|
|
|
|
|
|
|
|
|
|
/* Retrieving the probe's address. */
|
|
|
|
|
ret->p.address = extract_typed_address (&el->data[0], ptr_type);
|
|
|
|
|
|
|
|
|
|
/* Link-time sh_addr of `.stapsdt.base' section. */
|
|
|
|
|
base_ref = extract_typed_address (&el->data[size], ptr_type);
|
|
|
|
|
|
|
|
|
|
/* Semaphore address. */
|
|
|
|
|
ret->sem_addr = extract_typed_address (&el->data[2 * size], ptr_type);
|
|
|
|
|
|
|
|
|
|
ret->p.address += (ANOFFSET (objfile->section_offsets,
|
|
|
|
|
SECT_OFF_TEXT (objfile))
|
|
|
|
|
+ base - base_ref);
|
|
|
|
|
if (ret->sem_addr)
|
|
|
|
|
ret->sem_addr += (ANOFFSET (objfile->section_offsets,
|
|
|
|
|
SECT_OFF_DATA (objfile))
|
|
|
|
|
+ base - base_ref);
|
|
|
|
|
|
|
|
|
|
/* Arguments. We can only extract the argument format if there is a valid
|
|
|
|
|
name for this probe. */
|
|
|
|
|
probe_args = memchr (ret->p.name, '\0',
|
|
|
|
|
(char *) el->data + el->size - ret->p.name);
|
|
|
|
|
|
|
|
|
|
if (probe_args != NULL)
|
|
|
|
|
++probe_args;
|
|
|
|
|
|
|
|
|
|
if (probe_args == NULL || (memchr (probe_args, '\0',
|
|
|
|
|
(char *) el->data + el->size - ret->p.name)
|
|
|
|
|
!= el->data + el->size - 1))
|
|
|
|
|
{
|
|
|
|
|
complaint (&symfile_complaints, _("corrupt probe argument when "
|
|
|
|
|
"reading `%s'"), objfile->name);
|
|
|
|
|
/* If the argument string is NULL, it means some problem happened with
|
|
|
|
|
it. So we return 0. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret->args_parsed = 0;
|
|
|
|
|
ret->args_u.text = (void *) probe_args;
|
|
|
|
|
|
|
|
|
|
/* Successfully created probe. */
|
|
|
|
|
VEC_safe_push (probe_p, *probesp, (struct probe *) ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Helper function which tries to find the base address of the SystemTap
|
|
|
|
|
base section named STAP_BASE_SECTION_NAME. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_stap_base_address_1 (bfd *abfd, asection *sect, void *obj)
|
|
|
|
|
{
|
|
|
|
|
asection **ret = obj;
|
|
|
|
|
|
|
|
|
|
if ((sect->flags & (SEC_DATA | SEC_ALLOC | SEC_HAS_CONTENTS))
|
|
|
|
|
&& sect->name && !strcmp (sect->name, STAP_BASE_SECTION_NAME))
|
|
|
|
|
*ret = sect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Helper function which iterates over every section in the BFD file,
|
|
|
|
|
trying to find the base address of the SystemTap base section.
|
|
|
|
|
Returns 1 if found (setting BASE to the proper value), zero otherwise. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
get_stap_base_address (bfd *obfd, bfd_vma *base)
|
|
|
|
|
{
|
|
|
|
|
asection *ret = NULL;
|
|
|
|
|
|
|
|
|
|
bfd_map_over_sections (obfd, get_stap_base_address_1, (void *) &ret);
|
|
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
|
{
|
|
|
|
|
complaint (&symfile_complaints, _("could not obtain base address for "
|
|
|
|
|
"SystemTap section on objfile `%s'."),
|
|
|
|
|
obfd->filename);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (base)
|
|
|
|
|
*base = ret->vma;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Helper function for `elf_get_probes', which gathers information about all
|
|
|
|
|
SystemTap probes from OBJFILE. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_get_probes (VEC (probe_p) **probesp, struct objfile *objfile)
|
|
|
|
|
{
|
|
|
|
|
/* If we are here, then this is the first time we are parsing the
|
|
|
|
|
SystemTap probe's information. We basically have to count how many
|
|
|
|
|
probes the objfile has, and then fill in the necessary information
|
|
|
|
|
for each one. */
|
|
|
|
|
bfd *obfd = objfile->obfd;
|
|
|
|
|
bfd_vma base;
|
|
|
|
|
struct sdt_note *iter;
|
|
|
|
|
unsigned save_probesp_len = VEC_length (probe_p, *probesp);
|
|
|
|
|
|
2012-05-08 03:35:35 +02:00
|
|
|
|
if (objfile->separate_debug_objfile_backlink != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* This is a .debug file, not the objfile itself. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (!elf_tdata (obfd)->sdt_note_head)
|
|
|
|
|
{
|
|
|
|
|
/* There isn't any probe here. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!get_stap_base_address (obfd, &base))
|
|
|
|
|
{
|
|
|
|
|
/* There was an error finding the base address for the section.
|
|
|
|
|
Just return NULL. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parsing each probe's information. */
|
|
|
|
|
for (iter = elf_tdata (obfd)->sdt_note_head; iter; iter = iter->next)
|
|
|
|
|
{
|
|
|
|
|
/* We first have to handle all the information about the
|
|
|
|
|
probe which is present in the section. */
|
|
|
|
|
handle_stap_probe (objfile, iter, probesp, base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (save_probesp_len == VEC_length (probe_p, *probesp))
|
|
|
|
|
{
|
|
|
|
|
/* If we are here, it means we have failed to parse every known
|
|
|
|
|
probe. */
|
|
|
|
|
complaint (&symfile_complaints, _("could not parse SystemTap probe(s) "
|
|
|
|
|
"from inferior"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_relocate (struct probe *probe_generic, CORE_ADDR delta)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe *probe = (struct stap_probe *) probe_generic;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
|
|
|
|
probe->p.address += delta;
|
|
|
|
|
if (probe->sem_addr)
|
|
|
|
|
probe->sem_addr += delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stap_probe_is_linespec (const char **linespecp)
|
|
|
|
|
{
|
|
|
|
|
static const char *const keywords[] = { "-pstap", "-probe-stap", NULL };
|
|
|
|
|
|
|
|
|
|
return probe_is_linespec_by_keyword (linespecp, keywords);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_gen_info_probes_table_header (VEC (info_probe_column_s) **heads)
|
|
|
|
|
{
|
|
|
|
|
info_probe_column_s stap_probe_column;
|
|
|
|
|
|
|
|
|
|
stap_probe_column.field_name = "semaphore";
|
|
|
|
|
stap_probe_column.print_name = _("Semaphore");
|
|
|
|
|
|
|
|
|
|
VEC_safe_push (info_probe_column_s, *heads, &stap_probe_column);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stap_gen_info_probes_table_values (struct probe *probe_generic,
|
|
|
|
|
VEC (const_char_ptr) **ret)
|
|
|
|
|
{
|
|
|
|
|
struct stap_probe *probe = (struct stap_probe *) probe_generic;
|
2012-07-18 18:12:17 +02:00
|
|
|
|
struct gdbarch *gdbarch;
|
2012-04-27 22:47:57 +02:00
|
|
|
|
const char *val = NULL;
|
|
|
|
|
|
|
|
|
|
gdb_assert (probe_generic->pops == &stap_probe_ops);
|
|
|
|
|
|
2012-07-18 18:12:17 +02:00
|
|
|
|
gdbarch = get_objfile_arch (probe->p.objfile);
|
|
|
|
|
|
2012-04-27 22:47:57 +02:00
|
|
|
|
if (probe->sem_addr)
|
|
|
|
|
val = print_core_address (gdbarch, probe->sem_addr);
|
|
|
|
|
|
|
|
|
|
VEC_safe_push (const_char_ptr, *ret, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* SystemTap probe_ops. */
|
|
|
|
|
|
|
|
|
|
static const struct probe_ops stap_probe_ops =
|
|
|
|
|
{
|
|
|
|
|
stap_probe_is_linespec,
|
|
|
|
|
stap_get_probes,
|
|
|
|
|
stap_relocate,
|
|
|
|
|
stap_get_probe_argument_count,
|
2013-07-24 21:50:32 +02:00
|
|
|
|
stap_can_evaluate_probe_arguments,
|
2012-04-27 22:47:57 +02:00
|
|
|
|
stap_evaluate_probe_argument,
|
|
|
|
|
stap_compile_to_ax,
|
|
|
|
|
stap_set_semaphore,
|
|
|
|
|
stap_clear_semaphore,
|
|
|
|
|
stap_probe_destroy,
|
|
|
|
|
stap_gen_info_probes_table_header,
|
|
|
|
|
stap_gen_info_probes_table_values,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Implementation of the `info probes stap' command. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
info_probes_stap_command (char *arg, int from_tty)
|
|
|
|
|
{
|
|
|
|
|
info_probes_for_ops (arg, from_tty, &stap_probe_ops);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _initialize_stap_probe (void);
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
_initialize_stap_probe (void)
|
|
|
|
|
{
|
|
|
|
|
VEC_safe_push (probe_ops_cp, all_probe_ops, &stap_probe_ops);
|
|
|
|
|
|
2012-08-02 11:36:40 +02:00
|
|
|
|
add_setshow_zuinteger_cmd ("stap-expression", class_maintenance,
|
|
|
|
|
&stap_expression_debug,
|
|
|
|
|
_("Set SystemTap expression debugging."),
|
|
|
|
|
_("Show SystemTap expression debugging."),
|
|
|
|
|
_("When non-zero, the internal representation "
|
|
|
|
|
"of SystemTap expressions will be printed."),
|
|
|
|
|
NULL,
|
|
|
|
|
show_stapexpressiondebug,
|
|
|
|
|
&setdebuglist, &showdebuglist);
|
2012-04-27 22:47:57 +02:00
|
|
|
|
|
|
|
|
|
create_internalvar_type_lazy ("_probe_argc", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) -1);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg0", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 0);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg1", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 1);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg2", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 2);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg3", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 3);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg4", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 4);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg5", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 5);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg6", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 6);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg7", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 7);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg8", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 8);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg9", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 9);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg10", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 10);
|
|
|
|
|
create_internalvar_type_lazy ("_probe_arg11", &probe_funcs,
|
|
|
|
|
(void *) (uintptr_t) 11);
|
|
|
|
|
|
|
|
|
|
add_cmd ("stap", class_info, info_probes_stap_command,
|
|
|
|
|
_("\
|
|
|
|
|
Show information about SystemTap static probes.\n\
|
|
|
|
|
Usage: info probes stap [PROVIDER [NAME [OBJECT]]]\n\
|
|
|
|
|
Each argument is a regular expression, used to select probes.\n\
|
|
|
|
|
PROVIDER matches probe provider names.\n\
|
|
|
|
|
NAME matches the probe names.\n\
|
|
|
|
|
OBJECT matches the executable or shared library name."),
|
|
|
|
|
info_probes_cmdlist_get ());
|
|
|
|
|
|
|
|
|
|
}
|