From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-f66.google.com (mail-wm1-f66.google.com [209.85.128.66]) by sourceware.org (Postfix) with ESMTPS id 6AA2838618F9 for ; Thu, 9 Jul 2020 11:57:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 6AA2838618F9 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=palves.net Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=alves.ped@gmail.com Received: by mail-wm1-f66.google.com with SMTP id g75so1477300wme.5 for ; Thu, 09 Jul 2020 04:57:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=n4Oh6alKcOxrnhjkfoOnK6tBz8P5k5Y+pyZh3eGh1sI=; b=T0hc9zlMO7UwxIR3/BIZph6wZsdlMmxVDROrC27m8YpQtpDsABphPDZ3Q6uVB4MKMv VxxAZHiSrzDJd78qNa1v3ZNFLfocYYmrTYBTsU74j5EQV+oqqw/YUyntYAcJIIotyJy4 IuTphxGbZCCYN162mX1FDobmcMSa+mkq/IbVrvR7AnDo/skik30bHdZbQLV3Tu8kS3s1 AhOfyzOknK9gfuEWVRKtSeiQdKYTlPvkJdIx4yS2YWbuUhCuk4JkWnWPLEksnHAjus8N L8glwoDWO1AaSq2f7Btag0fgUWUzsm0Sp8Ye58LVqsMp5ZhuwwBmxfbKo4ieU7vPAsny Ybbg== X-Gm-Message-State: AOAM5335ESHSC8lBdtWiFz7ZNuL3PKtb7QBPbcePCY5jmmOPfxDxzNvH 9vMbGLARcdDivZInP35857VHk0MY4IU= X-Google-Smtp-Source: ABdhPJxKzJ6g3NL89frsjf3TljX77vtY4o5SygJMG61XAlMSh6pHFMd3y1+eTbqz/6NBWOV6/YIXpw== X-Received: by 2002:a1c:7204:: with SMTP id n4mr13657520wmc.9.1594295820577; Thu, 09 Jul 2020 04:57:00 -0700 (PDT) Received: from ?IPv6:2001:8a0:f91a:c400:56ee:75ff:fe8d:232b? ([2001:8a0:f91a:c400:56ee:75ff:fe8d:232b]) by smtp.gmail.com with ESMTPSA id 59sm5511694wrj.37.2020.07.09.04.56.59 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 09 Jul 2020 04:56:59 -0700 (PDT) Subject: Re: [PATCH 3/3] Make scoped_restore_current_thread's cdtors exception free (RFC) To: Simon Marchi , gdb-patches@sourceware.org References: <20200708233125.1030-1-pedro@palves.net> <20200708233125.1030-4-pedro@palves.net> <58a4020f-fbbb-611f-eb23-c1b3fa25d4f2@simark.ca> From: Pedro Alves Message-ID: Date: Thu, 9 Jul 2020 12:56:58 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.2.1 MIME-Version: 1.0 In-Reply-To: <58a4020f-fbbb-611f-eb23-c1b3fa25d4f2@simark.ca> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-9.3 required=5.0 tests=BAYES_00, FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_DMARC_STATUS, KAM_LOTSOFHASH, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 09 Jul 2020 11:57:05 -0000 On 7/9/20 4:49 AM, Simon Marchi wrote: > On 2020-07-08 7:31 p.m., Pedro Alves wrote: >> If the remote target closes while we're reading registers/memory for >> restoring the selected frame in scoped_restore_current_thread's dtor, >> the corresponding TARGET_CLOSE_ERROR error is swallowed by the >> scoped_restore_current_thread's dtor, because letting exceptions >> escape from a dtor is bad. It isn't great to lose that errors like >> that, though. I've been thinking about how to avoid it, and I came up >> with this patch. >> >> The idea here is to make scoped_restore_current_thread's dtor do as >> little as possible, to avoid any work that might throw in the first >> place. And to do that, instead of having the dtor call >> restore_selected_frame, which re-finds the previously selected frame, >> just record the frame_id/level of the desired selected frame, and have >> get_selected_frame find the frame the next time it is called. In >> effect, this implements most of Cagney's suggestion, here: >> >> /* On demand, create the selected frame and then return it. If the >> selected frame can not be created, this function prints then throws >> an error. When MESSAGE is non-NULL, use it for the error message, >> otherwize use a generic error message. */ >> /* FIXME: cagney/2002-11-28: At present, when there is no selected >> frame, this function always returns the current (inner most) frame. >> It should instead, when a thread has previously had its frame >> selected (but not resumed) and the frame cache invalidated, find >> and then return that thread's previously selected frame. */ >> extern struct frame_info *get_selected_frame (const char *message); >> >> The only thing missing to fully implement that would be to make >> reinit_frame_cache just clear selected_frame instead of calling >> select_frame(NULL), and the call select_frame(NULL) explicitly in the >> places where we really wanted reinit_frame_cache to go back to the >> current frame too. That can done separately, though, I'm not >> proposing to do that in this patch. >> >> restore_selected_frame should really move from thread.c to frame.c, >> but I didn't do that here, just to avoid churn in the patch while it >> collects comments. I will do that as a preparatory patch if people >> agree with this approach. >> >> Incidentally, this patch alone would fix the crashes fixed by the >> previous patches in the series, because with this, >> scoped_restore_current_thread's constructor doesn't throw either. > > I don't understand all the code changes, the but the idea sounds fine > to me. > >> -/* Select frame FI (or NULL - to invalidate the current frame). */ >> +/* Select frame FI (or NULL - to invalidate the selected frame). */ >> >> void >> select_frame (struct frame_info *fi) >> { >> selected_frame = fi; >> + selected_frame_level = frame_relative_level (fi); >> + if (selected_frame_level == 0) >> + { >> + /* Treat the current frame especially -- we want to always >> + save/restore it without warning, even if the frame ID changes >> + (see restore_selected_frame). Also get_frame_id may access >> + the target's registers/memory, and thus skipping get_frame_id >> + optimizes the common case. */ >> + selected_frame_level = -1; >> + selected_frame_id = null_frame_id; >> + } >> + else >> + selected_frame_id = get_frame_id (fi); >> + > > I don't really understand this part, why don't we want to set selected_frame_level > and selected_frame_id when the level is 0. I'm more interested by why it wouldn't > be correct or how it would break things, rather than the optimization aspect. > > Simon > At first, I was recording frame 0 normally, without that special case. But running the testsuite revealed regressions in a couple testcases: gdb.python/py-unwind-maint.exp gdb.server/bkpt-other-inferior.exp Both are related to the get_frame_id call. Before the patch, get_frame_id isn't called on the current frame until you try to backtrace from it. Adding the get_frame_id call makes the gdb.python/py-unwind-maint.exp testcase print the Python unwinder callbacks in a different order, unexpected by the testcase. I didn't look too deeply into this one, but I suspect it would just be a matter of adjusting the testcase's expectations. The gdb.server/bkpt-other-inferior.exp one though is what got me thinking. The testcase makes sure that setting a breakpoint in a function that doesn't exist in the remote inferior does not cause remote protocol traffic. After the patch, without the special casing, the testcase would fail because the get_frame_id call, coming from check_frame_language_change # called after every command -> get_selected_frame -> restore_selected_frame -> select_frame(get_current_frame()) -> get_frame_id would cause registers and memory to be read from the remote target (when restoring the selected frame). Those accesses aren't wrong, but they aren't the kind that the bug the testcase is looking for. Those were about spurious/incorrect remote protocol accesses when parsing the function's prologue. Neither of these cases were strictly incorrect, though they got me thinking, and I came to the conclusion that warning when we fail to re-find the current frame is pointless, and that avoids having unbreak the testcases mentioned, or even redo them differently in the gdb.server/bkpt-other-inferior.exp case. I've updated the comment to make it clearer with an example. I've also polished the patch some more. I now renamed the current restore_selected_frame to lookup_selected_frame, to give space to the new save_selected_frame/restore_selected_frame pair. select_frame_lazy is now restore_selected_frame. save_selected_frame/restore_selected_frame are now noexcept, and their intro comments explain why. I declared lookup_selected_frame in frame.h already, thinking that it's easier if I move lookup_selected_frame from thread.c to frame.c after this is in, instead of before. I rewrote most of the comments. For example, I think the selected_frame_id/selected_frame_level/selected_frame comments are now much clearer. And I made scoped_restore_selected_frame save/restore the language too. I was only doing that in scoped_restore_current_thread before. Let me know what you think of this version. >From 1353dbd062debba4056c2413678d3438f7713ca3 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Thu, 9 Jul 2020 11:31:29 +0100 Subject: [PATCH] Make scoped_restore_current_thread's cdtors exception free (RFC) If the remote target closes while we're reading registers/memory for restoring the selected frame in scoped_restore_current_thread's dtor, the corresponding TARGET_CLOSE_ERROR error is swallowed by the scoped_restore_current_thread's dtor, because letting exceptions escape from a dtor is bad. It isn't great to lose that errors like that, though. I've been thinking about how to avoid it, and I came up with this patch. The idea here is to make scoped_restore_current_thread's dtor do as little as possible, to avoid any work that might throw in the first place. And to do that, instead of having the dtor call restore_selected_frame, which re-finds the previously selected frame, just record the frame_id/level of the desired selected frame, and have get_selected_frame find the frame the next time it is called. In effect, this implements most of Cagney's suggestion, here: /* On demand, create the selected frame and then return it. If the selected frame can not be created, this function prints then throws an error. When MESSAGE is non-NULL, use it for the error message, otherwize use a generic error message. */ /* FIXME: cagney/2002-11-28: At present, when there is no selected frame, this function always returns the current (inner most) frame. It should instead, when a thread has previously had its frame selected (but not resumed) and the frame cache invalidated, find and then return that thread's previously selected frame. */ extern struct frame_info *get_selected_frame (const char *message); The only thing missing to fully implement that would be to make reinit_frame_cache just clear selected_frame instead of calling select_frame(NULL), and the call select_frame(NULL) explicitly in the places where we really wanted reinit_frame_cache to go back to the current frame too. That can done separately, though, I'm not proposing to do that in this patch. Note that this patch renames restore_selected_frame to lookup_selected_frame, and adds a new restore_selected_frame function that doesn't throw, to be paired with the also-new save_selected_frame function. lookup_selected_frame should really move from thread.c to frame.c, but I didn't do that here, just to avoid churn in the patch while it collects comments. I did make it extern and declared it in frame.h already, preparing for the move. I will do the move as a follow up patch if people agree with this approach. Incidentally, this patch alone would fix the crashes fixed by the previous patches in the series, because with this, scoped_restore_current_thread's constructor doesn't throw either. gdb/ChangeLog: * blockframe.c (block_innermost_frame): Use get_selected_frame. * frame.c (scoped_restore_selected_frame::scoped_restore_selected_frame): Use save_selected_frame. Save language as well. (scoped_restore_selected_frame::~scoped_restore_selected_frame): Use restore_selected_frame, and restore language as well. (selected_frame_id, selected_frame_level): New. (selected_frame): Update comments. (save_selected_frame, restore_selected_frame): New. (get_selected_frame): Use lookup_selected_frame. (get_selected_frame_if_set): Delete. (select_frame): Record selected_frame_level and selected_frame_id. * frame.h (scoped_restore_selected_frame) : New fields. (get_selected_frame_if_set): Delete declaration. (select_frame): Update comments. (save_selected_frame, restore_selected_frame) (lookup_selected_frame): Declare. * gdbthread.h (scoped_restore_current_thread) : New field. * stack.c (select_frame_command_core, frame_command_core): Use get_selected_frame. * thread.c (restore_selected_frame): Rename to ... (lookup_selected_frame): ... this and make extern. Select the current frame if the frame level is -1. (scoped_restore_current_thread::restore): Also restore the language. (scoped_restore_current_thread::~scoped_restore_current_thread): Don't try/catch. (scoped_restore_current_thread::scoped_restore_current_thread): Save the language as well. Use save_selected_frame. --- gdb/blockframe.c | 6 +--- gdb/frame.c | 101 +++++++++++++++++++++++++++++++++++++++++++------------ gdb/frame.h | 38 +++++++++++++++++---- gdb/gdbthread.h | 4 +++ gdb/stack.c | 9 +++-- gdb/thread.c | 67 +++++++++--------------------------- 6 files changed, 137 insertions(+), 88 deletions(-) diff --git a/gdb/blockframe.c b/gdb/blockframe.c index 05c26bc2c2..706b6db92c 100644 --- a/gdb/blockframe.c +++ b/gdb/blockframe.c @@ -448,14 +448,10 @@ find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr) struct frame_info * block_innermost_frame (const struct block *block) { - struct frame_info *frame; - if (block == NULL) return NULL; - frame = get_selected_frame_if_set (); - if (frame == NULL) - frame = get_current_frame (); + frame_info *frame = get_selected_frame (NULL); while (frame != NULL) { const struct block *frame_block = get_frame_block (frame, NULL); diff --git a/gdb/frame.c b/gdb/frame.c index ff27b9f00e..aab501ad67 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -297,17 +297,15 @@ frame_stash_invalidate (void) /* See frame.h */ scoped_restore_selected_frame::scoped_restore_selected_frame () { - m_fid = get_frame_id (get_selected_frame (NULL)); + m_lang = current_language->la_language; + save_selected_frame (&m_fid, &m_level); } /* See frame.h */ scoped_restore_selected_frame::~scoped_restore_selected_frame () { - frame_info *frame = frame_find_by_id (m_fid); - if (frame == NULL) - warning (_("Unable to restore previously selected frame.")); - else - select_frame (frame); + restore_selected_frame (m_fid, m_level); + set_language (m_lang); } /* Flag to control debugging. */ @@ -1641,10 +1639,51 @@ get_current_frame (void) } /* The "selected" stack frame is used by default for local and arg - access. May be zero, for no selected frame. */ - + access. */ + +/* The "single source of truth" for the selected frame is the + SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL pair. Frame IDs can be + saved/restored across reinitializing the frame cache, while + frame_info pointers can't (frame_info objects are invalidated). If + we know the corresponding frame_info object, it is cached in + SELECTED_FRAME. If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are + null_ptid / -1, and the target has stack and is stopped, the + selected frame is the current (innermost) frame, otherwise there's + no selected frame. */ +static frame_id selected_frame_id = null_frame_id; +static int selected_frame_level = -1; + +/* The cached frame_info object pointing to the selected frame. + Looked up on demand by get_selected_frame. */ static struct frame_info *selected_frame; +/* See frame.h. */ + +void +save_selected_frame (frame_id *frame_id, int *frame_level) + noexcept +{ + *frame_id = selected_frame_id; + *frame_level = selected_frame_level; +} + +/* See frame.h. */ + +void +restore_selected_frame (frame_id a_frame_id, int frame_level) + noexcept +{ + /* get_selected_frame_info never returns level == 0, so we shouldn't + see it here either. */ + gdb_assert (frame_level != 0); + + selected_frame_id = a_frame_id; + selected_frame_level = frame_level; + + /* Will be looked up latter by get_seleted_frame. */ + selected_frame = nullptr; +} + int has_stack_frames (void) { @@ -1682,24 +1721,14 @@ get_selected_frame (const char *message) { if (message != NULL && !has_stack_frames ()) error (("%s"), message); - /* Hey! Don't trust this. It should really be re-finding the - last selected frame of the currently selected thread. This, - though, is better than nothing. */ - select_frame (get_current_frame ()); + + lookup_selected_frame (selected_frame_id, selected_frame_level); } /* There is always a frame. */ gdb_assert (selected_frame != NULL); return selected_frame; } -/* If there is a selected frame, return it. Otherwise, return NULL. */ - -struct frame_info * -get_selected_frame_if_set (void) -{ - return selected_frame; -} - /* This is a variant of get_selected_frame() which can be called when the inferior does not have a frame; in that case it will return NULL instead of calling error(). */ @@ -1712,12 +1741,42 @@ deprecated_safe_get_selected_frame (void) return get_selected_frame (NULL); } -/* Select frame FI (or NULL - to invalidate the current frame). */ +/* Select frame FI (or NULL - to invalidate the selected frame). */ void select_frame (struct frame_info *fi) { selected_frame = fi; + selected_frame_level = frame_relative_level (fi); + if (selected_frame_level == 0) + { + /* Treat the current frame especially -- we want to always + save/restore it without warning, even if the frame ID changes + (see lookup_selected_frame). E.g.: + + // The current frame is selected, the target had just stopped. + { + scoped_restore_selected_frame restore_frame; + some_operation_that_changes_the_stack (); + } + // scoped_restore_selected_frame's dtor runs, but the + // original frame_id can't be found. No matter whether it + // is found or not, we still end up with the now-current + // frame selected. Warning in lookup_selected_frame in this + // case seems pointless. + + Also get_frame_id may access the target's registers/memory, + and thus skipping get_frame_id optimizes the common case. + + Saving the selected frame this way makes get_selected_frame + and restore_current_frame return/re-select whatever frame is + the innermost (current) then. */ + selected_frame_level = -1; + selected_frame_id = null_frame_id; + } + else + selected_frame_id = get_frame_id (fi); + /* NOTE: cagney/2002-05-04: FI can be NULL. This occurs when the frame is being invalidated. */ diff --git a/gdb/frame.h b/gdb/frame.h index e835d49f9c..97d50b645c 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -181,8 +181,14 @@ class scoped_restore_selected_frame private: - /* The ID of the previously selected frame. */ + /* The ID and level of the previously selected frame. */ struct frame_id m_fid; + int m_level; + + /* Save/restore the language as well, because selecting a frame + changes the current language to the frame's language if "set + language auto". */ + enum language m_lang; }; /* Methods for constructing and comparing Frame IDs. */ @@ -329,13 +335,33 @@ extern void reinit_frame_cache (void); and then return that thread's previously selected frame. */ extern struct frame_info *get_selected_frame (const char *message); -/* If there is a selected frame, return it. Otherwise, return NULL. */ -extern struct frame_info *get_selected_frame_if_set (void); - -/* Select a specific frame. NULL, apparently implies re-select the - inner most frame. */ +/* Select a specific frame. NULL implies re-select the inner most + frame. */ extern void select_frame (struct frame_info *); +/* Save the frame ID and frame level of the selected frame in FRAME_ID + and FRAME_LEVEL, to be restored later with restore_selected_frame. + This is preferred over getting the same info out of + get_selected_frame directly because this function does not create + the selected-frame's frame_info object if it hasn't been created + yet, and thus doesn't throw. */ +extern void save_selected_frame (frame_id *frame_id, int *frame_level) + noexcept; + +/* Restore selected frame as saved with save_selected_frame. Does not + try to find the corresponding frame_info object. Instead the next + call to get_selected_frame will look it up and cache the result. + This function does not throw, it is designed to be safe to called + from the destructors of RAII types. */ +extern void restore_selected_frame (frame_id frame_id, int frame_level) + noexcept; + +/* Lookup the frame_info object for the selected frame FRAME_ID / + FRAME_LEVEL and cache the result. If FRAME_LEVEL > 0 and the + originally selected frame isn't found, warn and select the + innermost (current) frame. */ +extern void lookup_selected_frame (frame_id frame_id, int frame_level); + /* Given a FRAME, return the next (more inner, younger) or previous (more outer, older) frame. */ extern struct frame_info *get_prev_frame (struct frame_info *); diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 0166b2000f..edfdf98b3d 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -667,6 +667,10 @@ class scoped_restore_current_thread frame_id m_selected_frame_id; int m_selected_frame_level; bool m_was_stopped; + /* Save/restore the language as well, because selecting a frame + changes the current language to the frame's language if "set + language auto". */ + enum language m_lang; }; /* Returns a pointer into the thread_info corresponding to diff --git a/gdb/stack.c b/gdb/stack.c index 265e764dc2..93de451a12 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -1836,9 +1836,9 @@ trailing_outermost_frame (int count) static void select_frame_command_core (struct frame_info *fi, bool ignored) { - struct frame_info *prev_frame = get_selected_frame_if_set (); + frame_info *prev_frame = get_selected_frame (NULL); select_frame (fi); - if (get_selected_frame_if_set () != prev_frame) + if (get_selected_frame (NULL) != prev_frame) gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); } @@ -1857,10 +1857,9 @@ select_frame_for_mi (struct frame_info *fi) static void frame_command_core (struct frame_info *fi, bool ignored) { - struct frame_info *prev_frame = get_selected_frame_if_set (); - + frame_info *prev_frame = get_selected_frame (nullptr); select_frame (fi); - if (get_selected_frame_if_set () != prev_frame) + if (get_selected_frame (nullptr) != prev_frame) gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME); else print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME); diff --git a/gdb/thread.c b/gdb/thread.c index a3c2be7dd0..e0b49abf0c 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1325,20 +1325,25 @@ switch_to_thread (process_stratum_target *proc_target, ptid_t ptid) switch_to_thread (thr); } -static void -restore_selected_frame (struct frame_id a_frame_id, int frame_level) +/* See frame.h. */ + +void +lookup_selected_frame (struct frame_id a_frame_id, int frame_level) { struct frame_info *frame = NULL; int count; - /* This means there was no selected frame. */ + /* This either means there was no selected frame, or the selected + frame was the innermost (the current frame). */ if (frame_level == -1) { - select_frame (NULL); + select_frame (get_current_frame ()); return; } - gdb_assert (frame_level >= 0); + /* select_frame never saves 0 in selected_frame_level, so we + shouldn't see it here. */ + gdb_assert (frame_level > 0); /* Restore by level first, check if the frame id is the same as expected. If that fails, try restoring by frame id. If that @@ -1409,22 +1414,14 @@ scoped_restore_current_thread::restore () && target_has_stack && target_has_memory) restore_selected_frame (m_selected_frame_id, m_selected_frame_level); + + set_language (m_lang); } scoped_restore_current_thread::~scoped_restore_current_thread () { if (!m_dont_restore) - { - try - { - restore (); - } - catch (const gdb_exception &ex) - { - /* We're in a dtor, there's really nothing else we can do - but swallow the exception. */ - } - } + restore (); if (m_thread != NULL) m_thread->decref (); @@ -1436,47 +1433,15 @@ scoped_restore_current_thread::scoped_restore_current_thread () m_inf = current_inferior (); m_inf->incref (); + m_lang = current_language->la_language; + if (inferior_ptid != null_ptid) { m_thread = inferior_thread (); m_thread->incref (); - struct frame_info *frame; - m_was_stopped = m_thread->state == THREAD_STOPPED; - if (m_was_stopped - && target_has_registers - && target_has_stack - && target_has_memory) - { - /* When processing internal events, there might not be a - selected frame. If we naively call get_selected_frame - here, then we can end up reading debuginfo for the - current frame, but we don't generally need the debuginfo - at this point. */ - frame = get_selected_frame_if_set (); - } - else - frame = NULL; - - try - { - m_selected_frame_id = get_frame_id (frame); - m_selected_frame_level = frame_relative_level (frame); - } - catch (const gdb_exception_error &ex) - { - m_selected_frame_id = null_frame_id; - m_selected_frame_level = -1; - - /* Better let this propagate. */ - if (ex.error == TARGET_CLOSE_ERROR) - { - m_thread->decref (); - m_inf->decref (); - throw; - } - } + save_selected_frame (&m_selected_frame_id, &m_selected_frame_level); } else m_thread = NULL; base-commit: ad8464f799a4c96c7ab8bdfec3f95846cf54f9b0 prerequisite-patch-id: 32ffdda7d7d774bc4df88bf848bcb796559b53ce prerequisite-patch-id: 02021b74355b70debd344a6e445285c67dfef7d6 prerequisite-patch-id: c87fcf5a54f6805967cbf8ab107606c57d9ecf52 prerequisite-patch-id: ac7dee583d0ffa519c9d1cd89d27664bca68d8c1 prerequisite-patch-id: eac59ae2ea85d2d51e5be1b03e88a5641cc12c22 prerequisite-patch-id: 13da42ad04dc8e2e3bd6a556a0be0e17cf23669b prerequisite-patch-id: fd3f09fdb58ddc1c595ea014716851f4c8fca48c prerequisite-patch-id: 8230262cb24020a5b37e915843180e940807ba1f prerequisite-patch-id: 59bcf41af4057da60e15e3f35fae1fd4e0bf36b9 -- 2.14.5