From patchwork Mon Nov 3 14:44:43 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Galvan X-Patchwork-Id: 3567 Received: (qmail 5835 invoked by alias); 3 Nov 2014 14:44:49 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 5818 invoked by uid 89); 3 Nov 2014 14:44:48 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.6 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_SOFTFAIL autolearn=no version=3.3.2 X-HELO: mail-lb0-f169.google.com Received: from mail-lb0-f169.google.com (HELO mail-lb0-f169.google.com) (209.85.217.169) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Mon, 03 Nov 2014 14:44:47 +0000 Received: by mail-lb0-f169.google.com with SMTP id p9so3734264lbv.0 for ; Mon, 03 Nov 2014 06:44:43 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to :content-type:content-transfer-encoding; bh=axVjxCRmrxqhlJiv7pJUAT7sdNcZVWjlcKu1htHXtzc=; b=HfGYUCx0q906vQSxZhW+MFWRasu2lKGIjTH5T+VDc0oR6keP604svr4/F05gD8uv8f Q4McPiZP6ekieAeaHWasFzvJ33aHn6+GoYbksJxZo4MC7V3fNf1s454sYXxxa+uGSgkY AZkkyI/KK//FrsaNoKYKgqiRa25heYhLGJJQmYMMVEIeoRZI3mXMvOaxPhD5BAT2g1PO Hi5k0mXDq94X1ITrEfICYPRBVewgLiNXNp29kVIekRk6iBuHapW2EYrb/jYCiEDdAcZy xWzNBPJbU5hYevntzfr8ih+fDJGet3tR3wg/z27upTqxMQmoMlHlT9lY/BJfSrAReQnB kb5Q== X-Gm-Message-State: ALoCoQm31weDafBu0MH8hlIkqwqPRgrXzfOdiufnH8RLWZ2NNunFLcNKNBF3jGKgDT+jEGx0JZRZ MIME-Version: 1.0 X-Received: by 10.112.146.37 with SMTP id sz5mr51164774lbb.27.1415025883150; Mon, 03 Nov 2014 06:44:43 -0800 (PST) Received: by 10.112.59.129 with HTTP; Mon, 3 Nov 2014 06:44:43 -0800 (PST) Date: Mon, 3 Nov 2014 11:44:43 -0300 Message-ID: Subject: [RFC][PATCH v2] Python API: Add gdb.stack_may_be_invalid From: Martin Galvan To: gdb-patches@sourceware.org, Doug Evans , Eli Zaretskii , Ulrich Weigand , Pedro Alves , Daniel Gutson This is a continuation from this thread: https://www.sourceware.org/ml/gdb-patches/2014-10/msg00575.html When single-stepping through machine instructions in non-optimized code, sometimes the values of local variables may be invalid because the stack frame is either not completely set up yet (in the function's prologue) or destroyed (usually in the epilogue). This patch aims to bring a way to determine whether this may happen at a given PC without actually running the program. In the previous thread we came to the conclusion that the notions of "prologue" and "epilogue" don't exist in optimized code as they do in unoptimized code. We shouldn't even have to worry about local variables being unaccessible as the DWARF information is good enough. However, the problem still remains in unoptimized code, so we'd still need a solution for that case. Initially, I used the now deprecated in_prologue function. Pedro Alves suggested looking at how function breakpoints work instead (e.g. "break myFunction" will place the breakpoint at the end of myFunction's prologue). So I started reading the code and doing some tests, and these are some of the things I saw: 1) As Gdb relies on the compiler placing the prologue on its own "line" (that is, emmiting line info that advances only the address but not the actual line), at many points in the code there's a check for single-line functions, as people probably thought there may be a problem with that behavior. That seems to be wrong, and was the cause of some of my initial confusion. If you look at the DWARF info for one such function, you'll see something like: Special opcode 75: advance Address by 10 to 0x3b2 and Line by 0 to 38 Doing "break myFunction" will correctly place the breakpoint at 0x3b2. 2) The behavior of handle_step_into_function and setting breakpoints is inconsistent for optimized code, at least in ARM. If you step into a function in a program compiled with gcc -O1, you'll see the PC ends up one instruction after the set of instructions that place the arguments passed as registers in the registers they'll be used in. If you do "break myFunction", however, the breakpoint will correctly be placed at the very first instruction. Both handle_step.. and setting breakpoints have the same effect on -O0 code. 3) This is the most important one: if you compare the DWARF info from a -O0 binary to the one from a -O1 you'll notice the variables that appear to be wrong in the prologue have a negative DW_OP_fbreg in their DW_AT_location for -O0, and a location list for -O1. I believe the presence of this location list is what makes the local variables in -O1 always reliable. The locations_valid field in struct symtab has the following comment (in symtab.h): /* Symtab has been compiled with both optimizations and debug info so that GDB may stop skipping prologues as variables locations are valid already at function entry points. */ The locations_valid field is checked in skip_prologue_sal, which is called by find_function_start_sal if its "funfirstline" parameter is non-zero. According to the comment in symtab.c: "If the argument FUNFIRSTLINE is nonzero, we want the first line of real code inside the function." If we look at how "break myFunction" works, we'll see that we end up calling find_function_start_sal to determine at which PC we have to place our breakpoint. Therefore, that's the function we should be calling when checking whether the stack frame will be valid at a prologue, as it also accounts for optimizations. As before, we take a conservative approach: we'll only be returning False if we're certain that the local variables will be accessible at the given PC. I changed the function names appropriately, and merged both the prologue and epilogue functions into one. The start address of the function is no longer a parameter, as we can get it from the debug info itself. I'm sending this patch as a RFC since I'd like to know what everyone thinks of it before I bring in the documentation and test cases. As before, thanks a lot for your feedback. static PyObject * @@ -2000,6 +2062,13 @@ Return the selected inferior object." }, { "inferiors", gdbpy_inferiors, METH_NOARGS, "inferiors () -> (gdb.Inferior, ...).\n\ Return a tuple containing all inferiors." }, + + { "stack_may_be_invalid", gdbpy_stack_may_be_invalid, METH_VARARGS, + "stack_may_be_invalid (Long) -> Boolean.\n\ +Returns True if a given PC may point to an address in which the stack frame\n\ +may not be valid (either because it may not be set up yet or because it was\n\ +destroyed, usually in a function's epilogue), False otherwise."}, + {NULL, NULL, 0, NULL} }; diff --git a/gdb/python/python.c b/gdb/python/python.c index 41999d6..76c3f36 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -33,6 +33,7 @@ #include "python.h" #include "extension-priv.h" #include "cli/cli-utils.h" +#include "block.h" #include /* Declared constants and enum for python stack printing. */ @@ -703,6 +704,67 @@ gdbpy_solib_name (PyObject *self, PyObject *args) return str_obj; } +/* Returns 1 if the given PC may be inside a prologue, 0 if it definitely isn't + and -1 if we have no debug info to use. */ + +static int +pc_may_be_in_prologue (gdb_py_ulongest pc) +{ + struct symbol *function_symbol; + struct symtab_and_line function_body_start_sal; + int result = -1; + + function_symbol = find_pc_function(pc); + + if (function_symbol) + { + function_body_start_sal = find_function_start_sal (function_symbol, 1); + + result = pc < function_body_start_sal.pc; + } + + return result; +} + +static int +stack_is_destroyed (gdb_py_ulongest pc) +{ + return gdbarch_in_function_epilogue_p (python_gdbarch, pc); +} + +/* Returns True if a given PC may point to an address in which the stack frame + may not be valid (either because it may not be set up yet or because it was + destroyed, usually in a function's epilogue), False otherwise. */ + +static PyObject * +gdbpy_stack_may_be_invalid (PyObject *self, PyObject *args) +{ + gdb_py_ulongest pc; + PyObject *result = NULL; + int pc_maybe_in_prologue; + + if (PyArg_ParseTuple (args, GDB_PY_LLU_ARG, &pc)) + { + pc_maybe_in_prologue = pc_may_be_in_prologue (pc); + + if (pc_maybe_in_prologue != -1) + { + result = stack_is_destroyed (pc) || pc_maybe_in_prologue ? + Py_True : Py_False; + + Py_INCREF (result); + } + else /* No debug info at that point. */ + { + PyErr_Format (PyExc_RuntimeError, + _("There's no debug info for a function that could be\n" + " enclosing the given PC.")); + } + } + + return result; +} + /* A Python function which is a wrapper for decode_line_1. */