From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id SYvnKHlQRGJaLQAAWB0awg (envelope-from ) for ; Wed, 30 Mar 2022 08:43:37 -0400 Received: by simark.ca (Postfix, from userid 112) id 9659A1F0BB; Wed, 30 Mar 2022 08:43:37 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,MAILING_LIST_MULTI, RDNS_DYNAMIC,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id B1E431E787 for ; Wed, 30 Mar 2022 08:43:36 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2430E3888C43 for ; Wed, 30 Mar 2022 12:43:36 +0000 (GMT) Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) by sourceware.org (Postfix) with ESMTPS id 1A0413858D3C for ; Wed, 30 Mar 2022 12:43:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1A0413858D3C Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=palves.net Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wr1-f50.google.com with SMTP id j18so29090602wrd.6 for ; Wed, 30 Mar 2022 05:43:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=P86VdO8Bf+odbq9nXRCrH5djvy2Mhl1/DpIR2IpGg/Y=; b=FJKCAQBNB9xSt3S1wpk5ecTgp+EQtu+eWqLDcqT0QbMlQ5ufw+20bqY28qTzcCJaOb K2H+C30nTFl80Lal88YBteMH7XRraKCTujfUj9boIZFOMfJGiZk0TURfHu6q8LjjuWyu FnougyhvTvJB6wDWUtE6bUXwPD7XZnyjN0G/aA9dENmUVLtgA2u6k25qLywkbjBGGBB4 fIpFGltjkMDM1wjO6Jrg9jq64g3k4+pX5kRYcz6TX1NsYPZy3W7beB89rB6Ig6QnnL6e tByo11+k0MNIySY9JDslJIaVoDBSMCLStg85ZUI1oQvB4Cktrao/h564c5mLo2oZ0DuG deqA== X-Gm-Message-State: AOAM533+AM7yJo4zLxXZDYZxU1cpPhxJ73EjiYQbDzHO+CWtWmF7LZt0 /2haZIwNDyHI/mFYgTx7/iVF15XCKg8= X-Google-Smtp-Source: ABdhPJwLRkIbv5+to61GCtPnDLC29ZKOmo94J8eNELDsFzNAsW0Xcxd/s8XaV3yUuntMAv6pPnfAGA== X-Received: by 2002:a5d:4205:0:b0:205:7acf:d111 with SMTP id n5-20020a5d4205000000b002057acfd111mr36391816wrq.444.1648644202073; Wed, 30 Mar 2022 05:43:22 -0700 (PDT) Received: from localhost ([2001:8a0:f924:2600:209d:85e2:409e:8726]) by smtp.gmail.com with ESMTPSA id x3-20020a5d6b43000000b001e317fb86ecsm17074169wrw.57.2022.03.30.05.43.20 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 30 Mar 2022 05:43:20 -0700 (PDT) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 0/3] gdbserver/linux: access memory while threads are running without pausing Date: Wed, 30 Mar 2022 13:43:16 +0100 Message-Id: <20220330124319.2804582-1-pedro@palves.net> X-Mailer: git-send-email 2.26.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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: , Errors-To: gdb-patches-bounces+public-inbox=simark.ca@sourceware.org Sender: "Gdb-patches" While working on teaching GDBserver to handle stepping-over-thread-exit better, I stumbled on the following scenario: If you set a conditional breakpoint, and condition evaluation is done on the target (which it is against gdbserver), GDB uploads the agent expression for the condition to GDBserver. When the breakpoint is hit, GDBserver evaluates the condition, and if false, GDBserver steps over the breakpoint itself, all without GDB involvement. That single-step is done in-line, GDBserver doesn't know how to do displaced stepping. That means that all threads are paused temporarily for the single-step, the breakpoint is removed, and when the single-step finishes, the breakpoint instruction is inserted again. Now, if that breakpoint was set on top of an exit syscall instruction, then when the single-step over the breakpoint finishes, and GDBserver tries to re-insert the breakpoint, the thread that was stepping is gone. And thus you get something like this: ... [threads] wait_1: Hit a gdbserver breakpoint. [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x22 [threads] ax_vdebug: gdbserver/ax: Op const8 -> sp=1, top=0x0 [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x27 [threads] ax_vdebug: gdbserver/ax: At end of expression, sp=1, stack top cache=0x0 [threads] wait_1: Hit a gdbserver breakpoint. [threads] wait_1: proceeding all threads. [threads] thread_needs_step_over: Need step over [LWP 864044]? Ignoring, not stopped [threads] thread_needs_step_over: Need step over [LWP 864578]? Ignoring, not stopped [threads] thread_needs_step_over: Need step over [LWP 864579]? Ignoring, has pending status. [threads] get_pc: pc is 0x5555555552a8 [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x22 [threads] ax_vdebug: gdbserver/ax: Op const8 -> sp=1, top=0x0 [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x27 [threads] ax_vdebug: gdbserver/ax: At end of expression, sp=1, stack top cache=0x0 [threads] thread_needs_step_over: Need step over [LWP 864580]? yes, found breakpoint at 0x5555555552a8 [threads] proceed_all_lwps: found thread 864580 needing a step-over [threads] start_step_over: Starting step-over on LWP 864580. Stopping all threads [threads] stop_all_lwps: enter ... [threads] stop_all_lwps: exit [threads] start_step_over: Done stopping all threads for step-over. ... [threads] wait_for_event_filtered: waitpid(-1, ...) returned 864580, ERRNO-OK [threads] wait_for_event_filtered: waitpid 864580 received 0 (exited) [threads] filter_event: 864580 exited [threads] finish_step_over: Finished step over. [threads] insert_memory_breakpoint: Failed to read shadow memory of breakpoint at 0x5555555552a8 (No such process). [threads] reinsert_raw_breakpoint: Failed to reinsert breakpoint at 0x5555555552a8 (-1). [threads] reinsert_fast_tracepoint_jumps_at: Could not find fast tracepoint jump at 0x5555555552a8 in list (reinserting). [threads] delete_lwp: deleting 864580 ... Note the: [threads] insert_memory_breakpoint: Failed to read shadow memory of breakpoint at 0x5555555552a8 (No such process). [threads] reinsert_raw_breakpoint: Failed to reinsert breakpoint at 0x5555555552a8 (-1). lines. GDB fails to reinsert the breakpoint, because the event thread is gone. PTRACE_PEEKTEXT/PTRACE_POKETEXT don't work if you pass it a dead thread. OTOH, currently, from GDB's perspective, GDBserver/Linux does support accessing memory while the program is running. This is important for e.g., for being able to set breakpoints while the inferior is running, in non-stop mode. The way this is implemented today, is that when handling any remote protocol packet that requires accessing memory, GDBserver temporarily pauses all threads. This is done by wrapping memory accesses with prepare_to_access_memory/done_accessing_memory. prepare_to_access_memory ultimately calls into the target backend, which does whatever is necessary to be able to prepare for accessing memory. For Linux, that currently translates to pausing everything, via target_pause_all. No other port supports non-stop, so these prepare_to_access_memory/done_accessing_memory calls do nothing for all other ports. Now, this memory-access wrapping is only done in GDB-facing code. Internally in linux-low.cc, when handling an event, we tend to know that the current thread is stopped, so we usually can access memory without pausing everything else. However, in the scenario detailed above, the eventing thread has exited. We would need to find some other stopped thread to write memory through, and if none found, stop some. I guess you see where I'm going with this. It's so much simpler if we teach GDBserver to always access memory via /proc/PID/mem. Then we don't need to worry about whether the current thread is running, or is gone. We just access memory and it Just Works. So that's what this series does -- it teaches GDBserver to always access memory via /proc/PID/mem, and then eliminates the whole prepare_to_access_memory mechanism. Even without the scenario detailed above, not having to pause threads all the time just makes sense. Pedro Alves (3): gdbserver/qXfer::threads, prepare_to_access_memory=>target_pause_all gdbserver/linux: Access memory even if threads are running gdbserver: Eliminate prepare_to_access_memory gdbserver/linux-low.cc | 237 ++++++++++++++-------------------------- gdbserver/linux-low.h | 11 +- gdbserver/mem-break.cc | 101 +++-------------- gdbserver/server.cc | 81 +++++--------- gdbserver/target.cc | 94 ---------------- gdbserver/target.h | 21 ---- gdbserver/tracepoint.cc | 13 +-- 7 files changed, 131 insertions(+), 427 deletions(-) base-commit: 5321c31bc78379a33f07dc7bef9256d05b942ad7 -- 2.26.2