From patchwork Thu Dec 22 17:31:04 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Griffin X-Patchwork-Id: 88888 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp2924373qgi; Thu, 22 Dec 2016 09:34:25 -0800 (PST) X-Received: by 10.84.173.195 with SMTP id p61mr21411228plb.68.1482428065822; Thu, 22 Dec 2016 09:34:25 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id r4si31381814pgr.239.2016.12.22.09.34.25 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 22 Dec 2016 09:34:25 -0800 (PST) Received-SPF: pass (google.com: domain of gdb-patches-return-135881-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@sourceware.org; spf=pass (google.com: domain of gdb-patches-return-135881-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gdb-patches-return-135881-patch=linaro.org@sourceware.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; q=dns; s=default; b=CV0AHK/xuJSPiAPccqX4t5SA3Y6mQCf F+dQ/adVhPgg+Co7BFc7xiWfI7GeYKRzIurQBrZTFLacb4aIgRU9RQvu26YGu2kA /c9y7fZrlM/RUMQAhgRdsgwBpnyJ21UitzXZZuJvg94F57dfI6Xv8X2z3HloWYMY EtAR3PvF80QM= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; s=default; bh=nyZfcM4C80S/3NsaBU504zxJwKI=; b=asyT7 hmEPz0izNLTzP+EHIxTcLHcV7vJgyhl92DiyuXoPPHT0zVIufJBW8cG0uVP0S5B0 O99498DeeC0enPQSAAwy9wM8UB0JgpiE7kF3Gj/vUdTTOPdekT60SvRFzVrSo7UL WMfNMRaNrpKS8pOatKWl/fpzk6UC3GPraMzPVA= Received: (qmail 70053 invoked by alias); 22 Dec 2016 17:34:11 -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 70035 invoked by uid 89); 22 Dec 2016 17:34:10 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.6 required=5.0 tests=AWL, BAYES_50, KAM_STOCKGEN, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=no version=3.3.2 spammy=banner, obtaining, calculating, joel X-HELO: mail-wm0-f54.google.com Received: from mail-wm0-f54.google.com (HELO mail-wm0-f54.google.com) (74.125.82.54) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 22 Dec 2016 17:33:59 +0000 Received: by mail-wm0-f54.google.com with SMTP id g23so181717070wme.1 for ; Thu, 22 Dec 2016 09:33:42 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=UaR4wUuGdGbJFAVLxPwc77uLr5XGTtiXXuPTqIT73II=; b=T2jQXbIo7eVvraKB9ch8XyLFwCbw3NsA1lwaEcpzatKYJT1cZemYCfR9CiY4KqUBfr hpBJpGpUeZFvHlGWw81jUJvcQ6BwkhzsPH1m2pcg9TYoYy1RTHbbZIGigc5q807TB/9Y ryG/n1HPZ1O2faeuTE2rpacjbirz74IEbikK7hvzvG155sm7tdW9UFispjW5UEzGy2xt EV6oXOb+8B1eA3w7p4PZ0kLIH9yAKMXFyzA6KtH1ab7iqoigQxNFrZtnVB+WIal2R7tN 7uxmvDgF2xgjwFZ+2KrL1thyu4hqs/FWRgruWbdN1Abr7YwLRhE9ZSXm7uA/qzTkcTXB 3+mQ== X-Gm-Message-State: AIkVDXJl/wp4+FN4X0VSMMMwRFxkI4+kKAIgBg/53lGoBwypodEJ6faqmxzQSJGOXg5LUF8/ X-Received: by 10.28.206.11 with SMTP id e11mr219862wmg.88.1482428017646; Thu, 22 Dec 2016 09:33:37 -0800 (PST) Received: from localhost.localdomain (cpc89244-aztw30-2-0-cust4998.18-1.cable.virginm.net. [86.31.179.135]) by smtp.gmail.com with ESMTPSA id o132sm33156178wmo.17.2016.12.22.09.33.35 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 22 Dec 2016 09:33:36 -0800 (PST) From: Peter Griffin To: gdb-patches@sourceware.org, palves@redhat.com, brobecker@adacore.com, kevinb@redhat.com, cagney@gnu.org, dje@google.com, drow@false.org, kettenis@gnu.org, yao.qi@arm.com, stanshebs@google.com, Ulrich.Weigand@de.ibm.com, elena.zannoni@oracle.com, eliz@gnu.org Cc: peter.griffin@linaro.org, arnez@linux.vnet.ibm.com Subject: [PATCH] Add Linux kernel thread runtime support. Date: Thu, 22 Dec 2016 17:31:04 +0000 Message-Id: <1482427864-4317-2-git-send-email-peter.griffin@linaro.org> In-Reply-To: <1482427864-4317-1-git-send-email-peter.griffin@linaro.org> References: <1482427864-4317-1-git-send-email-peter.griffin@linaro.org> This patch implements a new linux-kthread target_ops stratum which supports the Linux kernel thread runtime. It allows anything that the Linux kernel has created a task_struct for to be represented as a GDB thread object. This allows a user using GDB to debug the Linux kernel to see all the sleeping threads in the system rather than just physical CPU's (as is the case currently) and then use the GDB contextual commands such as 'thread' to easily switch between all the threads in the system, inspect data structures and get backtraces etc. e.g. (gdb) info threads Id Target Id Frame * 1 [swapper/0] (pid: 0 tgid: 0 ) cpu_v7_do_idle () at /sources/linux/arch/arm/mm/proc-v7.S:75 2 init (pid: 1 tgid: 1) context_switch (cookie=..., next=, prev=, rq=) at /sources/linux/kernel/sched/core.c:2902 3 [kthreadd] (pid: 2 tgid: 2) context_switch (cookie=..., next=, prev=, rq=) at /sources/linux/kernel/sched/core.c:2902 90 getty (pid: 1584 tgid: 1584) context_switch (cookie=..., next=, prev=, rq=) at /sources/linux/kernel/sched/core.c:2902 91 udevd (pid: 1586 tgid: 1586) context_switch (cookie=..., next=, prev=, rq=) at /sources/linux/kernel/sched/core.c:2902 linux-kthread.ch is split between the linux-kthread target_ops stratum methods themselves which are fairly self explanatory, helper functions which parse kernel data structures such as CPU runqueue for idle and curr tasks and a series of low level helper functions and macros which make obtaining symbol information and calculating struct field offsets of the various Linux kernel data structures much easier. Architecture support is implemented in the -linux-kthread files, and is currently limited to ARM, although more architecture support is expected to follow. Adding new architecture support is fairly trivial and mainly centres around populating the register cache of a sleeping thread from what the kernel saved on the stack when the thread was de-scheduled. gdb/ChangeLog (Peter Griffin): * linux-kthread.c, linux-kthread.h, arm-linux-kthread.c, arm-linux-kthread.h: New files. * configure.tgt: Add linux-kthread.o and arm-linux-kthread.o to gdb_target_obs. * Makefile.in: Add arm-linux-kthread.o and linux-kthread.o to ALL_TARGET_OBS. * gdbarch.sh (linux_kthread_arch_ops): New * gdbarch.c, gdbarch.h: Re-generated. * arm-tdep.c (arm_gdbarch_init): Call register_arm_linux_kthread_ops. Signed-off-by: Peter Griffin --- gdb/ChangeLog | 12 + gdb/Makefile.in | 8 +- gdb/arm-linux-kthread.c | 178 +++++ gdb/arm-linux-kthread.h | 27 + gdb/arm-tdep.c | 4 + gdb/configure.tgt | 6 +- gdb/gdbarch.c | 23 + gdb/gdbarch.h | 5 + gdb/gdbarch.sh | 3 + gdb/linux-kthread.c | 1828 +++++++++++++++++++++++++++++++++++++++++++++++ gdb/linux-kthread.h | 223 ++++++ 11 files changed, 2311 insertions(+), 6 deletions(-) create mode 100644 gdb/arm-linux-kthread.c create mode 100644 gdb/arm-linux-kthread.h create mode 100644 gdb/linux-kthread.c create mode 100644 gdb/linux-kthread.h -- 2.7.4 diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1fc5823..941e8e2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2016-12-22 Peter Griffin + + * linux-kthread.c, linux-kthread.h, arm-linux-kthread.c, + arm-linux-kthread.h: New files. + * configure.tgt: Add linux-kthread.o and arm-linux-kthread.o to + gdb_target_obs. + * Makefile.in: Add arm-linux-kthread.o and linux-kthread.o to + ALL_TARGET_OBS. + * gdbarch.sh (linux_kthread_arch_ops): New + * gdbarch.c, gdbarch.h: Re-generated. + * arm-tdep.c (arm_gdbarch_init): Call register_arm_linux_kthread_ops. + 2016-10-07 Joel Brobecker * version.in: Set GDB version number to 7.12. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 7b2df86..242dee5 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -657,7 +657,7 @@ ALL_64_TARGET_OBS = \ # All other target-dependent objects files (used with --enable-targets=all). ALL_TARGET_OBS = \ - armbsd-tdep.o arm.o arm-linux.o arm-linux-tdep.o \ + armbsd-tdep.o arm.o arm-linux.o arm-linux-tdep.o arm-linux-kthread.o\ arm-get-next-pcs.o arm-symbian-tdep.o \ armnbsd-tdep.o armobsd-tdep.o \ arm-tdep.o arm-wince-tdep.o \ @@ -676,6 +676,7 @@ ALL_TARGET_OBS = \ i386-sol2-tdep.o i386-tdep.o i387-tdep.o \ i386-dicos-tdep.o i386-darwin-tdep.o \ iq2000-tdep.o \ + linux-kthread.o \ linux-tdep.o \ lm32-tdep.o \ m32c-tdep.o \ @@ -990,7 +991,7 @@ common/common-exceptions.h target/target.h common/symbol.h \ common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \ common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h nat/amd64-linux-siginfo.h\ nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \ -tid-parse.h ser-event.h \ +tid-parse.h ser-event.h linux-kthread.h \ common/signals-state-save-restore.h # Header files that already have srcdir in them, or which are in objdir. @@ -1678,7 +1679,7 @@ ALLDEPFILES = \ amd64-linux-nat.c amd64-linux-tdep.c \ amd64-sol2-tdep.c \ arm.c arm-get-next-pcs.c \ - arm-linux.c arm-linux-nat.c arm-linux-tdep.c \ + arm-linux.c arm-linux-nat.c arm-linux-tdep.c arm-linux-kthread.c \ arm-symbian-tdep.c arm-tdep.c \ armnbsd-nat.c armbsd-tdep.c armnbsd-tdep.c armobsd-tdep.c \ avr-tdep.c \ @@ -1712,6 +1713,7 @@ ALLDEPFILES = \ inf-ptrace.c \ ia64-libunwind-tdep.c \ linux-fork.c \ + linux-kthread.c \ linux-tdep.c \ linux-record.c \ lm32-tdep.c \ diff --git a/gdb/arm-linux-kthread.c b/gdb/arm-linux-kthread.c new file mode 100644 index 0000000..a4352ac --- /dev/null +++ b/gdb/arm-linux-kthread.c @@ -0,0 +1,178 @@ +/* Linux kernel thread ARM target support. + + Copyright (C) 2011-2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" +#include "gdbcore.h" +#include "regcache.h" +#include "inferior.h" +#include "arch/arm.h" +#include "arm-tdep.h" +#include "linux-kthread.h" +#include "arm-linux-kthread.h" + +/* Support for Linux kernel threads */ + +/* From Linux arm/include/asm/thread_info.h */ +static struct cpu_context_save +{ + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t sl; + uint32_t fp; + uint32_t sp; + uint32_t pc; +} cpu_cxt; + +/* This function gets the register values that the schedule() routine + * has stored away on the stack to be able to restart a sleeping task. + * + **/ + +static void +arm_linuxkthread_fetch_registers (struct regcache *regcache, + int regnum, CORE_ADDR task_struct) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + CORE_ADDR sp = 0; + gdb_byte buf[8]; + int i; + uint32_t cpsr; + uint32_t thread_info_addr; + + DECLARE_FIELD (thread_info, cpu_context); + DECLARE_FIELD (task_struct, stack); + + gdb_assert (regnum >= -1); + + /*get thread_info address */ + thread_info_addr = read_unsigned_field (task_struct, task_struct, stack, + byte_order); + + /*get cpu_context as saved by scheduled */ + read_memory ((CORE_ADDR) thread_info_addr + + F_OFFSET (thread_info, cpu_context), + (gdb_byte *) & cpu_cxt, sizeof (struct cpu_context_save)); + + regcache_raw_supply (regcache, ARM_PC_REGNUM, &cpu_cxt.pc); + regcache_raw_supply (regcache, ARM_SP_REGNUM, &cpu_cxt.sp); + regcache_raw_supply (regcache, ARM_FP_REGNUM, &cpu_cxt.fp); + + /*general purpose registers */ + regcache_raw_supply (regcache, 10, &cpu_cxt.sl); + regcache_raw_supply (regcache, 9, &cpu_cxt.r9); + regcache_raw_supply (regcache, 8, &cpu_cxt.r8); + regcache_raw_supply (regcache, 7, &cpu_cxt.r7); + regcache_raw_supply (regcache, 6, &cpu_cxt.r6); + regcache_raw_supply (regcache, 5, &cpu_cxt.r5); + regcache_raw_supply (regcache, 4, &cpu_cxt.r4); + + /* Fake a value for cpsr:T bit. */ +#define IS_THUMB_ADDR(addr) ((addr) & 1) + cpsr = IS_THUMB_ADDR(cpu_cxt.pc) ? arm_psr_thumb_bit (target_gdbarch ()) : 0; + regcache_raw_supply (regcache, ARM_PS_REGNUM, &cpsr); + + for (i = 0; i < gdbarch_num_regs (target_gdbarch ()); i++) + if (REG_VALID != regcache_register_status (regcache, i)) + /* Mark other registers as unavailable. */ + regcache_invalidate (regcache, i); +} + +static void +arm_linuxkthread_store_registers (const struct regcache *regcache, + int regnum, CORE_ADDR addr) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + /* TODO */ + gdb_assert (regnum >= -1); + gdb_assert (0); + +} + +/* get_unmapped_area() in linux/mm/mmap.c. */ +DECLARE_ADDR (get_unmapped_area); + +#define DEFAULT_PAGE_OFFSET 0xC0000000 + +void arm_linuxkthread_get_page_offset(CORE_ADDR *page_offset) +{ + const char *result = NULL; + + /* We can try executing a python command if it exists in the kernel + source, and parsing the result. + result = execute_command_to_string ("lx-pageoffset", 0); */ + + /* Find CONFIG_PAGE_OFFSET macro definition at get_unmapped_area symbol + in linux/mm/mmap.c. */ + + result = kthread_find_macro_at_symbol(&get_unmapped_area, + "CONFIG_PAGE_OFFSET"); + if (result) + { + *page_offset = strtol(result, (char **) NULL, 16); + } + else + { + /* Kernel is compiled without macro info so make an educated guess. */ + warning("Assuming PAGE_OFFSET is 0x%x. Disabling to_interrupt\n", + DEFAULT_PAGE_OFFSET); + /* PAGE_OFFSET can't be reliably determined so disable the target_ops + to_interrupt ability. This means target can onbly be halted via + a breakpoint set in the kernel, which will mean CPU is configured + for kernel memory view. */ + lkthread_disable_to_interrupt = 1; + *page_offset = DEFAULT_PAGE_OFFSET; + } + + return; +} + +static int arm_linuxkthread_is_kernel_address (const CORE_ADDR addr) +{ + static CORE_ADDR linux_page_offset; + + if (!linux_page_offset) + arm_linuxkthread_get_page_offset(&linux_page_offset); + + return (addr >= linux_page_offset) ? true : false; +} + +/* The linux_kthread_arch_ops for most ARM targets. */ + +static struct linux_kthread_arch_ops arm_linuxkthread_ops = +{ + arm_linuxkthread_fetch_registers, + arm_linuxkthread_store_registers, + arm_linuxkthread_is_kernel_address, +}; + +/* Register arm_linuxkthread_ops in GDBARCH. */ + +void +register_arm_linux_kthread_ops (struct gdbarch *gdbarch) +{ + set_gdbarch_linux_kthread_ops (gdbarch, &arm_linuxkthread_ops); +} diff --git a/gdb/arm-linux-kthread.h b/gdb/arm-linux-kthread.h new file mode 100644 index 0000000..93ead7d --- /dev/null +++ b/gdb/arm-linux-kthread.h @@ -0,0 +1,27 @@ +/* Linux kernel thread ARM target support. + + Copyright (C) 2012-2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef ARM_LINUX_KTHREAD_H +#define ARM_LINUX_KTHREAD_H + +struct gdbarch; + +extern void register_arm_linux_kthread_ops (struct gdbarch *gdbarch); + +#endif diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 2525bd8..b1b1cc2 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -48,6 +48,7 @@ #include "arch/arm.h" #include "arch/arm-get-next-pcs.h" #include "arm-tdep.h" +#include "arm-linux-kthread.h" #include "gdb/sim-arm.h" #include "elf-bfd.h" @@ -9531,6 +9532,9 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) user_reg_add (gdbarch, arm_register_aliases[i].name, value_of_arm_user_reg, &arm_register_aliases[i].regnum); + /* Provide a Linux Kernel threads implementation. */ + register_arm_linux_kthread_ops (gdbarch); + return gdbarch; } diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 7f1aac3..266cbee 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -45,7 +45,7 @@ aarch64*-*-linux*) # Target: AArch64 linux gdb_target_obs="aarch64-tdep.o aarch64-linux-tdep.o aarch64-insn.o \ arm.o arm-linux.o arm-get-next-pcs.o arm-tdep.o \ - arm-linux-tdep.o \ + arm-linux-tdep.o linux-kthread.o \ glibc-tdep.o linux-tdep.o solib-svr4.o \ symfile-mem.o linux-record.o" build_gdbserver=yes @@ -80,7 +80,7 @@ alpha*-*-*) am33_2.0*-*-linux*) # Target: Matsushita mn10300 (AM33) running Linux gdb_target_obs="mn10300-tdep.o mn10300-linux-tdep.o linux-tdep.o \ - solib-svr4.o" + linux-kthread.o solib-svr4.o" ;; arm*-wince-pe | arm*-*-mingw32ce*) @@ -92,7 +92,7 @@ arm*-wince-pe | arm*-*-mingw32ce*) arm*-*-linux*) # Target: ARM based machine running GNU/Linux gdb_target_obs="arm.o arm-linux.o arm-get-next-pcs.o arm-tdep.o \ - arm-linux-tdep.o glibc-tdep.o \ + arm-linux-tdep.o glibc-tdep.o linux-kthread.o arm-linux-kthread.o \ solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o" build_gdbserver=yes ;; diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index af7359e..f383e49 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -325,6 +325,7 @@ struct gdbarch gdbarch_core_info_proc_ftype *core_info_proc; gdbarch_iterate_over_objfiles_in_search_order_ftype *iterate_over_objfiles_in_search_order; struct ravenscar_arch_ops * ravenscar_ops; + struct linux_kthread_arch_ops * linux_kthread_ops; gdbarch_insn_is_call_ftype *insn_is_call; gdbarch_insn_is_ret_ftype *insn_is_ret; gdbarch_insn_is_jump_ftype *insn_is_jump; @@ -431,6 +432,7 @@ gdbarch_alloc (const struct gdbarch_info *info, gdbarch->gen_return_address = default_gen_return_address; gdbarch->iterate_over_objfiles_in_search_order = default_iterate_over_objfiles_in_search_order; gdbarch->ravenscar_ops = NULL; + gdbarch->linux_kthread_ops = NULL; gdbarch->insn_is_call = default_insn_is_call; gdbarch->insn_is_ret = default_insn_is_ret; gdbarch->insn_is_jump = default_insn_is_jump; @@ -677,6 +679,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of core_info_proc, has predicate. */ /* Skip verify of iterate_over_objfiles_in_search_order, invalid_p == 0 */ /* Skip verify of ravenscar_ops, invalid_p == 0 */ + /* Skip verify of linux_kthread_ops, invalid_p == 0 */ /* Skip verify of insn_is_call, invalid_p == 0 */ /* Skip verify of insn_is_ret, invalid_p == 0 */ /* Skip verify of insn_is_jump, invalid_p == 0 */ @@ -1114,6 +1117,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: iterate_over_regset_sections = <%s>\n", host_address_to_string (gdbarch->iterate_over_regset_sections)); fprintf_unfiltered (file, + "gdbarch_dump: linux_kthread_ops = %s\n", + host_address_to_string (gdbarch->linux_kthread_ops)); + fprintf_unfiltered (file, "gdbarch_dump: long_bit = %s\n", plongest (gdbarch->long_bit)); fprintf_unfiltered (file, @@ -4701,6 +4707,23 @@ set_gdbarch_ravenscar_ops (struct gdbarch *gdbarch, gdbarch->ravenscar_ops = ravenscar_ops; } +struct linux_kthread_arch_ops * +gdbarch_linux_kthread_ops (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of linux_kthread_ops, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_linux_kthread_ops called\n"); + return gdbarch->linux_kthread_ops; +} + +void +set_gdbarch_linux_kthread_ops (struct gdbarch *gdbarch, + struct linux_kthread_arch_ops * linux_kthread_ops) +{ + gdbarch->linux_kthread_ops = linux_kthread_ops; +} + int gdbarch_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index bc0f692..2b38688 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -1435,6 +1435,11 @@ extern void set_gdbarch_iterate_over_objfiles_in_search_order (struct gdbarch *g extern struct ravenscar_arch_ops * gdbarch_ravenscar_ops (struct gdbarch *gdbarch); extern void set_gdbarch_ravenscar_ops (struct gdbarch *gdbarch, struct ravenscar_arch_ops * ravenscar_ops); +/* Linux kthread arch-dependent ops. */ + +extern struct linux_kthread_arch_ops * gdbarch_linux_kthread_ops (struct gdbarch *gdbarch); +extern void set_gdbarch_linux_kthread_ops (struct gdbarch *gdbarch, struct linux_kthread_arch_ops * linux_kthread_ops); + /* Return non-zero if the instruction at ADDR is a call; zero otherwise. */ typedef int (gdbarch_insn_is_call_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr); diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index d8e0eeb..4d319c0 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -1095,6 +1095,9 @@ m:void:iterate_over_objfiles_in_search_order:iterate_over_objfiles_in_search_ord # Ravenscar arch-dependent ops. v:struct ravenscar_arch_ops *:ravenscar_ops:::NULL:NULL::0:host_address_to_string (gdbarch->ravenscar_ops) +# Linux kthread arch-dependent ops. +v:struct linux_kthread_arch_ops *:linux_kthread_ops:::NULL:NULL::0:host_address_to_string (gdbarch->linux_kthread_ops) + # Return non-zero if the instruction at ADDR is a call; zero otherwise. m:int:insn_is_call:CORE_ADDR addr:addr::default_insn_is_call::0 diff --git a/gdb/linux-kthread.c b/gdb/linux-kthread.c new file mode 100644 index 0000000..59ab75d --- /dev/null +++ b/gdb/linux-kthread.c @@ -0,0 +1,1828 @@ +/* Linux kernel-level threads support. + + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This module allows GDB to correctly enumerate Linux kernel threads + whilst debugging a Linux kernel. */ + +#include "defs.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "inferior.h" +#include "objfiles.h" +#include "observer.h" +#include "regcache.h" +#include "target.h" +#include "gdbcmd.h" + +#include "gdb_obstack.h" +#include "macroscope.h" +#include "symtab.h" + +#include "linux-kthread.h" + +/* Whether to emit debugging output related to targetops. */ +static int debug_linuxkthread_targetops=0; + +/* Whether to emit debugging output related to threads. */ +static int debug_linuxkthread_threads=0; + +/* Whether to emit debugging output related to symbol lookup */ +static int debug_linuxkthread_symbols=0; + +/* Forward declarations */ + +static linux_kthread_info_t *lkthread_get_threadlist (void); +static linux_kthread_info_t *lkthread_get_by_ptid (ptid_t ptid); +static linux_kthread_info_t *lkthread_get_by_task_struct (CORE_ADDR task); +static linux_kthread_info_t *lkthread_get_running (int core); +static CORE_ADDR lkthread_get_runqueues_addr (void); +static CORE_ADDR lkthread_get_rq_curr_addr (int core); +static void lkthread_init (void); +static void lkthread_free_threadlist(void); +static void lkthread_invalidate_threadlist (void); +static int lkthread_is_curr_task (linux_kthread_info_t * ps); +static int lkthread_refresh_threadlist (int core); + +/* Whether the cached Linux thread list needs refreshing */ +static int kthread_list_invalid; + +/* Whether target_ops to_interrupt is disabled */ +int lkthread_disable_to_interrupt=0; + +/* Save the linux_kthreads ops returned by linux_kthread_target. */ +static struct target_ops *linux_kthread_ops; + +/* Non-zero if the thread stratum implemented by this module is active. */ +static int linux_kthread_active; +static int linux_kthread_loaded; +static int linux_kthread_debug; + +/* the core that triggered the event (zero-based) */ +int stop_core = 0; + +struct linux_kthread_data +{ + /* the processes list from Linux perspective */ + linux_kthread_info_t *process_list = NULL; + + /* the process we stopped at in target_wait */ + linux_kthread_info_t *wait_process = NULL; + + /* __per_cpu_offset */ + CORE_ADDR *per_cpu_offset; + + /* array of cur_rq(cpu) on each cpu */ + CORE_ADDR *rq_curr; + + /*array of rq->idle on each cpu */ + CORE_ADDR *rq_idle; + + /* array of scheduled process on each core */ + linux_kthread_info_t **running_process = NULL; + + /* array of process_counts for each cpu used for process list + housekeeping */ + unsigned long *process_counts; + + /* Storage for the field layout and addresses already gathered. */ + struct field_info *field_info_list; + struct addr_info *addr_info_list; + + unsigned char *scratch_buf; + int scratch_buf_size; +}; + +/* Handle to global lkthread data. */ +static struct linux_kthread_data *lkthread_h; + +/* Helper function to convert ptid to a string. */ + +static char * +ptid_to_str (ptid_t ptid) +{ + static char str[32]; + snprintf (str, sizeof (str) - 1, "ptid %d: lwp %ld: tid %ld", + ptid_get_pid (ptid), ptid_get_lwp (ptid), ptid_get_tid (ptid)); + + return str; +} + +/* Symbol and Field resolution helper functions. */ + +/* Helper function called by ADDR macro to fetch the address of a symbol + declared using DECLARE_ADDR macro. */ + +int +lkthread_lookup_addr (struct addr_info *addr, int check) +{ + if (addr->bmsym.minsym) + return 1; + + addr->bmsym = lookup_minimal_symbol (addr->name, NULL, NULL); + + if (!addr->bmsym.minsym) + { + if (debug_linuxkthread_symbols) + fprintf_unfiltered (gdb_stdlog, "Checking for address of '%s' :" + "NOT FOUND\n", addr->name); + + if (!check) + error ("Couldn't find address of %s", addr->name); + return 0; + } + + /* Chain initialized entries for cleanup. */ + addr->next = lkthread_h->addr_info_list; + lkthread_h->addr_info_list = addr; + + if (debug_linuxkthread_symbols) + fprintf_unfiltered (gdb_stdlog, "%s address is %s\n", addr->name, + phex (BMSYMBOL_VALUE_ADDRESS (addr->bmsym), 4)); + + return 1; +} + +/* Helper for lkthread_lookup_field. */ + +static int +find_struct_field (struct type *type, char *field, int *offset, int *size) +{ + int i; + + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + if (!strcmp (FIELD_NAME (TYPE_FIELDS (type)[i]), field)) + break; + } + + if (i >= TYPE_NFIELDS (type)) + return 0; + + *offset = FIELD_BITPOS (TYPE_FIELDS (type)[i]) / TARGET_CHAR_BIT; + *size = TYPE_LENGTH (check_typedef (TYPE_FIELDS (type)[i].type)); + return 1; +} + +/* Called by F_OFFSET or F_SIZE to compute the description of a field + declared using DECLARE_FIELD. */ + +int +lkthread_lookup_field (struct field_info *f, int check) +{ + + if (f->type != NULL) + return 1; + + f->type = + lookup_symbol (f->struct_name, NULL, STRUCT_DOMAIN, NULL).symbol; + + if (!f->type) + { + f->type = lookup_symbol (f->struct_name, NULL, VAR_DOMAIN, + NULL).symbol; + + if (f->type && TYPE_CODE (check_typedef (SYMBOL_TYPE (f->type))) + != TYPE_CODE_STRUCT) + f->type = NULL; + + } + + if (f->type == NULL + || !find_struct_field (check_typedef (SYMBOL_TYPE (f->type)), + f->field_name, &f->offset, &f->size)) + { + f->type = NULL; + if (!check) + error ("No such field %s::%s\n", f->struct_name, f->field_name); + + return 0; + } + + /* Chain initialized entries for cleanup. */ + f->next = lkthread_h->field_info_list; + lkthread_h->field_info_list = f; + + if (debug_linuxkthread_symbols) + { + fprintf_unfiltered (gdb_stdlog, "Checking for 'struct %s' : OK\n", + f->struct_name); + fprintf_unfiltered (gdb_stdlog, "%s::%s => offset %i size %i\n", + f->struct_name, f->field_name, f->offset, f->size); + } + return 1; +} + +/* Cleanup all the field and address info that has been gathered. */ + +static void +lkthread_reset_fields_and_addrs (void) +{ + struct field_info *next_field = lkthread_h->field_info_list; + struct addr_info *next_addr = lkthread_h->addr_info_list; + + /* clear list of collected fields */ + while (next_field) + { + next_field = lkthread_h->field_info_list->next; + lkthread_h->field_info_list->type = NULL; + lkthread_h->field_info_list->next = NULL; + lkthread_h->field_info_list = next_field; + } + + /* clear list of collected addrs */ + while (next_addr) + { + next_addr = lkthread_h->addr_info_list->next; + lkthread_h->addr_info_list->bmsym.minsym = NULL; + lkthread_h->addr_info_list->bmsym.objfile = NULL; + lkthread_h->addr_info_list->next = NULL; + lkthread_h->addr_info_list = next_addr; + } +} + +/* This function checks for a macro definition at a particular symbol + PC location and returns the replacement string or NULL if not found. + It is used to allow linux-kthread debugger to hook on a kernel symbol + and find out a macro definition e.g. PAGE_OFFSET if the kernel has + been compiled with -g3. */ + +const char * +kthread_find_macro_at_symbol(struct addr_info *symbol, char *macroname) +{ + struct symtab_and_line sal; + struct macro_scope *ms = NULL; + struct macro_definition *d; + + if (debug_linuxkthread_symbols) + fprintf_filtered (gdb_stdout, "kthread_find_macro_at_symbol symbol=%s" + "macro %s\n", symbol->name, macroname); + if (!macroname) + { + printf_filtered("No macro name provided\n"); + return NULL; + } + + if (!HAS_ADDR_PTR(symbol)) + { + printf_filtered("symbol doesn't exist\n"); + return NULL; + } + + /* get symtab for the address of the symbol */ + sal = find_pc_line(ADDR_PTR(symbol), 0); + + /* get macro scope for that symtab */ + ms = sal_macro_scope (sal); + + if (!ms) + { + fprintf_filtered (gdb_stdout, "GDB has no preprocessor macro information" + "for %s. Compile with -g3\n", symbol->name); + return NULL; + } + + d = macro_lookup_definition (ms->file, ms->line, macroname); + xfree(ms); + + if (d) + { + return d->replacement; + } + else + { + fprintf_filtered (gdb_stdout, + "The macro `%s' has no definition as a C/C++" + " preprocessor macro at %s symbol\n" + "at ", macroname, symbol->name); + return NULL; + } +} + +/* Symbols for Process and Task list parsing. */ + +DECLARE_ADDR (init_pid_ns); +DECLARE_FIELD (pid_namespace, last_pid); + +DECLARE_ADDR (init_task); +DECLARE_FIELD (list_head, next); +DECLARE_FIELD (task_struct, active_mm); +DECLARE_FIELD (task_struct, mm); +DECLARE_FIELD (task_struct, tasks); +DECLARE_FIELD (task_struct, thread_group); +DECLARE_FIELD (task_struct, pid); +DECLARE_FIELD (task_struct, tgid); +DECLARE_FIELD (task_struct, prio); +DECLARE_FIELD (task_struct, comm); + +DECLARE_FIELD (rq, curr); +DECLARE_FIELD (rq, idle); +DECLARE_FIELD (rq, lock); +DECLARE_FIELD (raw_spinlock, magic); + +/* asm/generic/percpu.h + * per_cpu_offset() is the offset that has to be added to a + * percpu variable to get to the instance for a certain processor. + * Most arches use the __per_cpu_offset array for those offsets but + * some arches have their own ways of determining the offset (x86_64, s390). + */ + +DECLARE_ADDR (__per_cpu_offset); +DECLARE_ADDR (per_cpu__process_counts); +DECLARE_ADDR (process_counts); +DECLARE_ADDR (per_cpu__runqueues); +DECLARE_ADDR (runqueues); + +#define CORE_INVAL (-1) +int max_cores = CORE_INVAL; + +static int last_pid; + +/* Iterate_over_threads() callback. */ + +static int +find_thread_tid (struct thread_info *tp, void *arg) +{ + long tid = *(long*)arg; + + return (ptid_get_tid(tp->ptid) == tid); +} + +/* Iterate_over_threads() callback. */ + +static int +find_thread_swapper (struct thread_info *tp, void *arg) +{ + long core = *(long*)arg; + + if ((!ptid_get_tid(tp->ptid)) && (ptid_get_lwp(tp->ptid) == core)) + { + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, + "swapper found: tp=%p tp->ptid %s core=%ld\n", + tp, ptid_to_str(tp->ptid), core); + + return 1; + } + return 0; +} + +static void +proc_private_dtor (struct private_thread_info * dummy) +{ + /* nop, do not free. */ +} + +/* Creates the 'linux_kthread_info_t' for the task pointed to by the passed + task_struct address by reading from the targets memory. If task_struct + is zero it creates placeholder swapper entry. */ + +static void +lkthread_get_task_info (CORE_ADDR task_struct, linux_kthread_info_t ** ps, + int core) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + linux_kthread_info_t *l_ps; + size_t size; + unsigned char *task_name; + int i = 0; + long tid = 0; + ptid_t this_ptid; + + while (*ps && (*ps)->valid) + ps = &((*ps)->next); + + if (*ps == NULL) + *ps = XCNEW (linux_kthread_info_t); + + l_ps = *ps; + + if (task_struct == 0) + { + /* Create swapper entry. */ + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "Creating swapper for core %d ps=%p\n", + core, l_ps); + + /* Create a fake swapper entry now for the additional core + to keep the gdb_thread ordering. */ + l_ps->task_struct = 0; + l_ps->mm = 0; + l_ps->tgid = 0; + l_ps->prio = 0; + l_ps->core = -1; + + if (l_ps->comm) + { + xfree (l_ps->comm); + l_ps->comm = NULL; + } + l_ps->comm = xstrdup ("[swapper]"); + } + else + { + /* Populate linux_kthread_info_t entry by reading from + task_struct target memory. */ + size = F_OFFSET (task_struct, comm) + F_SIZE (task_struct, comm); + + task_name = lkthread_h->scratch_buf + F_OFFSET (task_struct, comm); + + /* Use scratch area for messing around with strings + to avoid static arrays and dispersed mallocs and frees. */ + gdb_assert (lkthread_h->scratch_buf); + gdb_assert (lkthread_h->scratch_buf_size >= size); + + /* The task_struct is not likely to change much from one kernel version + to another. Knowing that comm is one of the far fields, + try reading the task_struct in one go. */ + read_memory (task_struct, lkthread_h->scratch_buf, size); + + l_ps->task_struct = task_struct; + tid = extract_unsigned_field (lkthread_h->scratch_buf, task_struct, + pid, byte_order); + + l_ps->mm = extract_pointer_field (lkthread_h->scratch_buf, + task_struct, mm); + l_ps->active_mm = extract_pointer_field (lkthread_h->scratch_buf, + task_struct, active_mm); + l_ps->tgid = extract_unsigned_field (lkthread_h->scratch_buf, + task_struct, tgid, byte_order); + l_ps->prio = extract_unsigned_field (lkthread_h->scratch_buf, + task_struct, prio, byte_order); + /* For to_core_of_threads. */ + l_ps->core = core; + + /* Add square brackets to name for kernel threads. */ + if (!l_ps->mm) + { + int len = strlen ((char *)task_name); + *(task_name + len) = ']'; + *(task_name + len + 1) = '\0'; + *(--task_name) = '['; + } + + if (l_ps->comm) + { + xfree (l_ps->comm); + l_ps->comm = NULL; + } + l_ps->comm = xstrdup ((char*)task_name); + } + + if (core != CORE_INVAL) + { + /* Long usage to map to LWP. */ + long core_mapped = core + 1; + + /* swapper[core]. */ + gdb_assert (tid==0); + + this_ptid = ptid_build (ptid_get_pid(inferior_ptid), core_mapped, tid); + l_ps->gdb_thread = + iterate_over_threads (find_thread_swapper, &core_mapped); + } + else + { + /* lwp stores CPU core, tid stores linux + pid this matches gdbremote usage. */ + + this_ptid = ptid_build (ptid_get_pid(inferior_ptid), CORE_INVAL, tid); + + l_ps->gdb_thread = iterate_over_threads (find_thread_tid, &tid); + + /* Reset the thread core value, if existing. */ + if (l_ps->gdb_thread) + { + gdb_assert (!l_ps->gdb_thread->priv); + PTID_OF (l_ps).lwp = CORE_INVAL; + } + } + + /* Flag the new entry as valid. */ + l_ps->valid = 1; + + /* Add new GDB thread if not found. */ + if (!l_ps->gdb_thread) + { + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "allocate a new GDB thread\n"); + + /* Add with info so that pid_to_string works. */ + l_ps->gdb_thread = add_thread_with_info (this_ptid, + (struct private_thread_info *)l_ps); + } + + /* Forcibly update the private field, as some threads (like hw threads) + have already have been created without. This also indicates whether + the gdb_thread needs to be pruned or not. */ + + l_ps->gdb_thread->priv = (struct private_thread_info *)l_ps; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "ps: comm = %s ptid=%s\n" + ,l_ps->comm, ptid_to_str(PTID_OF (l_ps))); + + /* The process list freeing is not handled thanks to + this `private` facility, yet. */ + + l_ps->gdb_thread->private_dtor = proc_private_dtor; + + /* Keep trace of the last state to notify a change. */ + l_ps->old_ptid = PTID_OF (l_ps); +} + +/* Get the rq->curr task_struct address from the runqueue of the requested + CPU core. Function returns a cached copy if already obtained from + target memory. If no cached address is available it fetches it from + target memory. */ + +CORE_ADDR +lkthread_get_rq_curr_addr (int cpucore) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + int length = + TYPE_LENGTH (builtin_type (target_gdbarch ())->builtin_data_ptr); + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_get_rq_curr_addr core(%d)\n", + cpucore); + + /* If not already cached read from target. */ + if (!lkthread_h->rq_curr[cpucore]) + { + CORE_ADDR curr_addr = lkthread_get_runqueues_addr (); + if (!curr_addr) + return 0; + + curr_addr = curr_addr + (CORE_ADDR) lkthread_h->per_cpu_offset[cpucore] + + F_OFFSET (rq, curr); + + lkthread_h->rq_curr[cpucore] = + read_memory_unsigned_integer (curr_addr, length, byte_order); + } + + return lkthread_h->rq_curr[cpucore]; +} + +/* Return the address of runqueues either from runqueues + symbol or more likely per_cpu__runqueues symbol. */ + +static CORE_ADDR +lkthread_get_runqueues_addr (void) +{ + CORE_ADDR runqueues_addr; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_get_runqueues_addr\n"); + + if (HAS_ADDR (runqueues)) + { + runqueues_addr = ADDR (runqueues); + } + else + { + runqueues_addr = ADDR (per_cpu__runqueues); + } + + return runqueues_addr; +} + +/* Returns the 'linux_kthread_info_t' corresponding to the passed task_struct + address or NULL if not in the list. */ + +linux_kthread_info_t * +lkthread_get_by_task_struct (CORE_ADDR task_struct) +{ + linux_kthread_info_t *ps = lkthread_get_threadlist (); + + while ((ps != NULL) && (ps->valid == 1)) + { + if (ps->task_struct == task_struct) + return ps; + ps = ps->next; + } + + return NULL; +} + +/* Return the linux_kthread_info_t* for the process currently executing + on the CPU core or NULL if CPU core is invalid. */ + +linux_kthread_info_t * +lkthread_get_running (int core) +{ + linux_kthread_info_t **running_ps = lkthread_h->running_process; + linux_kthread_info_t *current = NULL; + CORE_ADDR rq_curr_taskstruct; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_get_running core=%d\n",core); + + if (core == CORE_INVAL) + return NULL; + + /* If not already cached, read from target. */ + if (running_ps[core] == NULL) + { + + /* Ensure we have a runqueues address. */ + gdb_assert (lkthread_get_runqueues_addr ()); + + /* Get rq->curr task_struct address for CPU core. */ + rq_curr_taskstruct = lkthread_get_rq_curr_addr (core); + + if (rq_curr_taskstruct) + { + /* smp cpu is initialized. */ + current = lkthread_get_by_task_struct (rq_curr_taskstruct); + + if (!current) + { + /* this task struct is not known yet AND was not seen + while running down the tasks lists, so this is presumably + the swapper of an secondary SMP core. */ + + current = + lkthread_get_by_ptid (ptid_build(ptid_get_pid(inferior_ptid), + core + 1, 0)); + + gdb_assert(current); + + current->task_struct = rq_curr_taskstruct; + } + else + { + /* Update the thread's lwp in thread_list if it exists and + wasn't scheduled so that tid makes sense for both the + gdbserver and infrun.c. */ + PTID_OF (current).lwp = core + 1; + } + + current->core = core; + running_ps[core] = current; + + } + } + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "running ps[%d]: comm = %s ptid=%s\n", + core, running_ps[core]->comm, + ptid_to_str(PTID_OF (running_ps[core]))); + + return running_ps[core]; +} + +/* Return 1 if the passed linux_kthread_info_t is currently executing + on the CPU. Otherwise return 0. */ + +int +lkthread_is_curr_task (linux_kthread_info_t * ps) +{ + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_proc_is_curr_task\n"); + + return (ps && (ps == lkthread_get_running (ps->core))); +} + +/* Get the runqueue idle task_struct address for the given CPU core. */ + +static CORE_ADDR +lkthread_get_rq_idle (int core) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + int length = TYPE_LENGTH (builtin_type (target_gdbarch ())->builtin_func_ptr); + CORE_ADDR curr_addr = lkthread_get_runqueues_addr (); + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "get_rq_idle core(%d)\n", core); + + if (!curr_addr || !HAS_FIELD (rq, idle)) + return 0; + + /* If not already cached read from target. */ + if (!lkthread_h->rq_idle[core]) + { + curr_addr += (CORE_ADDR) lkthread_h->per_cpu_offset[core] + + F_OFFSET (rq, idle); + + lkthread_h->rq_idle[core] = read_memory_unsigned_integer (curr_addr, + length, + byte_order); + } + + return lkthread_h->rq_idle[core]; +} + +static int +get_process_count (int core) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + CORE_ADDR curr_addr = (CORE_ADDR) lkthread_h->per_cpu_offset[core]; + int length = + TYPE_LENGTH (builtin_type (target_gdbarch ())->builtin_unsigned_long); + static int warned = 0; + int process_count; + + if (HAS_ADDR (process_counts)) + curr_addr += ADDR (process_counts); + else if (HAS_ADDR (per_cpu__process_counts)) + curr_addr += ADDR (per_cpu__process_counts); + else + { + /* Return a fake, changing value so the thread list will be + refreshed but in a less optimal way. */ + if (!warned) + fprintf_unfiltered (gdb_stdlog, "No `process_counts` symbol\n"); + + warned++; + return warned; + } + + process_count = read_memory_unsigned_integer (curr_addr, length, byte_order); + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "core(%d) curr_addr=0x%lx proc_cnt=%d\n", + core, curr_addr, process_count); + + return process_count; +} + +static int +get_last_pid (void) +{ + int new_last_pid = 0; + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + + if (HAS_ADDR (init_pid_ns)) + { + /* Since 2.6.23 */ + new_last_pid = read_signed_field (ADDR (init_pid_ns), + pid_namespace, last_pid, byte_order); + } + else + fprintf_unfiltered (gdb_stdlog, "No `init_pid_ns` symbol found\n"); + + return new_last_pid; +}; + +static void +lkthread_memset_percpu_data(int numcores) +{ + memset (lkthread_h->running_process, 0x0, + numcores * sizeof (linux_kthread_info_t *)); + memset (lkthread_h->rq_curr, 0x0, numcores * sizeof (CORE_ADDR)); + memset (lkthread_h->rq_idle, 0x0, numcores * sizeof (CORE_ADDR)); + memset (lkthread_h->per_cpu_offset, 0, numcores * sizeof (CORE_ADDR)); +} + +/* Allocate memory which is dependent on number of physical CPUs. */ + +static void +lkthread_alloc_percpu_data(int numcores) +{ + gdb_assert (numcores >= 1); + + lkthread_h->running_process = XNEWVEC (linux_kthread_info_t *, numcores); + lkthread_h->process_counts = XNEWVEC (unsigned long, numcores); + + lkthread_h->per_cpu_offset = XNEWVEC (CORE_ADDR, numcores); + lkthread_h->rq_curr = XNEWVEC (CORE_ADDR, numcores); + lkthread_h->rq_idle = XNEWVEC (CORE_ADDR, numcores); + + memset (lkthread_h->process_counts, 0, sizeof (unsigned long)); + lkthread_memset_percpu_data(numcores); +} + +/* Free memory allocated by lkthread_alloc_percpu_data(). */ + +static void +lkthread_free_percpu_data(int numcores) +{ + xfree(lkthread_h->running_process); + xfree(lkthread_h->process_counts); + xfree(lkthread_h->per_cpu_offset); + xfree(lkthread_h->rq_curr); + xfree(lkthread_h->rq_idle); +} + +void +lkthread_get_per_cpu_offsets(int numcores) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + int length = TYPE_LENGTH (builtin_type (target_gdbarch ())->builtin_data_ptr); + CORE_ADDR curr_addr = ADDR (__per_cpu_offset); + int core; + + + if (!HAS_ADDR (__per_cpu_offset)) + { + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "Assuming non-SMP kernel.\n"); + + return; + } + + for (core=0; core < numcores; core++) + { + if (!lkthread_h->per_cpu_offset[core]) + lkthread_h->per_cpu_offset[core] = + read_memory_unsigned_integer (curr_addr, length, byte_order); + + curr_addr += (CORE_ADDR) length; + + if (!lkthread_h->per_cpu_offset[core]) + { + warning ("Suspicious null per-cpu offsets," + " or wrong number of detected cores:\n" + "ADDR (__per_cpu_offset) = %s\nmax_cores = %d", + phex (ADDR (__per_cpu_offset),4), max_cores); + + break; + } + } + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "SMP kernel. %d cores detected\n", + numcores); +} + +/* Iterate_over_threads() callback to print thread info. */ + +static int +thread_print_info (struct thread_info *tp, void *ignored) +{ + fprintf_unfiltered (gdb_stdlog, "thread_info = 0x%p ptid = %s\n", + tp, ptid_to_str(tp->ptid)); + return 0; +} + + +/* Initialise and allocate memory for linux-kthread module. */ + +static void +lkthread_init (void) +{ + struct thread_info *th = NULL; + struct cleanup *cleanup; + int size = + TYPE_LENGTH (builtin_type (target_gdbarch ())->builtin_unsigned_long); + + /* Ensure thread list from beneath target is up to date. */ + cleanup = make_cleanup_restore_integer (&print_thread_events); + print_thread_events = 0; + update_thread_list (); + do_cleanups (cleanup); + + /* Count the h/w threads. */ + max_cores = thread_count (); + gdb_assert (max_cores); + + if (debug_linuxkthread_threads) + { + fprintf_unfiltered (gdb_stdlog, "lkthread_init() cores(%d) GDB" + "HW threads\n", max_cores); + iterate_over_threads (thread_print_info, NULL); + } + + /* Allocate per cpu data. */ + lkthread_alloc_percpu_data(max_cores); + + lkthread_get_per_cpu_offsets(max_cores); + + if (!lkthread_get_runqueues_addr () && (max_cores > 1)) + fprintf_unfiltered (gdb_stdlog, "Could not find the address of CPU" + " runqueues current context information maybe " + "less precise\n."); + + /* Invalidate the linux-kthread cached list. */ + lkthread_invalidate_threadlist (); +} + +/* Determines whether the cached Linux thread list needs + to be invalidated and rebuilt by inspecting the targets + memory. */ + +int +lkthread_refresh_threadlist (int cur_core) +{ + int core; + int new_last_pid; + linux_kthread_info_t *ps; + int do_invalidate = 0; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_refresh_threadlist (%d)\n", + cur_core); + + /* Reset running_process and rq->curr cached values as they will + always need to be refreshed. */ + memset (lkthread_h->running_process, 0, + max_cores * sizeof (linux_kthread_info_t *)); + memset (lkthread_h->rq_curr, 0, max_cores * sizeof (CORE_ADDR)); + + new_last_pid = get_last_pid (); + if (new_last_pid != last_pid) + { + do_invalidate = 1; + last_pid = new_last_pid; + } + + /* Check if a process exited. */ + for (core = 0; core < max_cores; core++) + { + int new_pcount = get_process_count (core); + + /* If primary core has no processes kernel hasn't started. */ + if (core == 0 && new_pcount == 0) + { + warning ("Primary core has no processes - has kernel started?\n"); + warning ("linux-kthread will deactivate\n"); + return 0; + } + + if (new_pcount != lkthread_h->process_counts[core]) + { + lkthread_h->process_counts[core] = new_pcount; + do_invalidate = 1; + } + } + + if (do_invalidate) + lkthread_invalidate_threadlist (); + + /* Update the process_list now, so that init_task is in there. */ + (void) lkthread_get_threadlist (); + + /* Call update_thread_list() to prune GDB threads which are no + longer linked to a Linux task. */ + + if (linux_kthread_active) + update_thread_list(); + + /* Set the running process + we now have a thread_list looking like this: + [1] = { 42000, 0, 1 } + [2] = { 42000, 0, 2 } + [3] = { 42000, 1, -1 } + .... + [N] = { 42000, PID_N, -1 } + Now set the tid according to the running core. */ + + for (core = 0; core < max_cores; core++) + lkthread_get_running (core); + + lkthread_h->wait_process = lkthread_get_running (cur_core); + + if (!lkthread_h->wait_process) + return 0; + + gdb_assert(lkthread_h->wait_process->gdb_thread); + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "wait_process comm=%s ptid= %s\n", + lkthread_h->wait_process->comm, + ptid_to_str(PTID_OF (lkthread_h->wait_process))); + + gdb_assert((linux_kthread_info_t *) lkthread_h->wait_process->gdb_thread->priv + == lkthread_h->wait_process); + + /* Notify ptid changed. */ + ps = lkthread_h->process_list; + while (ps && ps->valid) + { + if (ptid_get_tid(ps->old_ptid) != ptid_get_tid(PTID_OF (ps))) + { + observer_notify_thread_ptid_changed (ps->old_ptid, PTID_OF (ps)); + ps->old_ptid.tid = ptid_get_tid(PTID_OF (ps)); + } + ps = ps->next; + } + + switch_to_thread(PTID_OF (lkthread_h->wait_process)); + gdb_assert(lkthread_get_by_ptid(inferior_ptid) == lkthread_h->wait_process); + + return 1; +} + + +static CORE_ADDR +_next_task (CORE_ADDR p) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + CORE_ADDR cur_entry = read_unsigned_embedded_field (p, task_struct, tasks, + list_head, next, + byte_order); + + if (!cur_entry) + { + warning ("kernel task list contains NULL pointer"); + return 0; + } + + return container_of (cur_entry, task_struct, tasks); +} + +static CORE_ADDR +_next_thread (CORE_ADDR p) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + CORE_ADDR cur_entry = read_unsigned_embedded_field (p, task_struct, + thread_group, + list_head, next, + byte_order); + + if (!cur_entry) + { + warning ("kernel thread group list contains NULL pointer\n"); + return 0; + } + + return container_of (cur_entry, task_struct, thread_group); +} + +/* Iterate round linux task_struct linked list calling + lkthread_get_task_info() for each task_struct. Also + calls lkthread_get_task_info() for each CPU runqueue + idle task_struct to create swapper threads. */ + +static linux_kthread_info_t ** +lkthread_get_threadlist_helper (linux_kthread_info_t ** ps) +{ + struct linux_kthread_arch_ops *arch_ops = + gdbarch_linux_kthread_ops (target_gdbarch ()); + CORE_ADDR rq_idle_taskstruct; + CORE_ADDR g, t, init_task_addr; + int core = 0, i; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_get_threadlist_helper\n"); + + init_task_addr = ADDR (init_task); + g = init_task_addr; + + do + { + t = g; + do + { + + if (!arch_ops->is_kernel_address(t)) + { + warning ("parsing of task list stopped because of invalid address" + "%s", phex (t, 4)); + break; + } + + lkthread_get_task_info (t, ps, core /*zero-based */ ); + core = CORE_INVAL; + + if (ptid_get_tid (PTID_OF (*ps)) == 0) + { + /* This is init_task, let's insert the other cores swapper + now. */ + for (i = 1; i < max_cores; i++) + { + ps = &((*ps)->next); + rq_idle_taskstruct = lkthread_get_rq_idle (i); + lkthread_get_task_info (rq_idle_taskstruct, ps, i); + } + } + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "Got task info for %s (%li)\n", + (*ps)->comm, ptid_get_lwp (PTID_OF (*ps))); + + ps = &((*ps)->next); + + /* Mark end of chain and remove those threads that disappeared + from the thread_list to avoid any_thread_of_process() to + select a ghost. */ + if (*ps) + (*ps)->valid = 0; + + t = _next_thread (t); + } while (t && (t != g)); + + g = _next_task (g); + } while (g && (g != init_task_addr)); + + return ps; +} + +/*----------------------------------------------------------------------------*/ + +/* This function returns a the list of 'linux_kthread_info_t' corresponding + to the tasks in the kernel's task list. */ + +static linux_kthread_info_t * +lkthread_get_threadlist (void) +{ + /* Return the cached copy if there is one, + or rebuild it. */ + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_getthread_list\n"); + + if (lkthread_h->process_list && lkthread_h->process_list->valid) + return lkthread_h->process_list; + + gdb_assert (kthread_list_invalid); + + lkthread_get_threadlist_helper (&lkthread_h->process_list); + + kthread_list_invalid = FALSE; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "kthread_list_invalid (%d)\n", + kthread_list_invalid); + + return lkthread_h->process_list; +} + +/* Returns a valid 'linux_kthread_info_t' corresponding to + the passed ptid or NULL if not found. NULL means + the thread needs to be pruned. */ + +linux_kthread_info_t *lkthread_get_by_ptid (ptid_t ptid) +{ + struct thread_info *tp; + long tid = ptid_get_tid(ptid); + long lwp = ptid_get_lwp(ptid); + linux_kthread_info_t *ps; + + /* Check list is valid. */ + gdb_assert(!kthread_list_invalid); + + if (tid) + { + /* non-swapper, tid is Linux pid. */ + tp = iterate_over_threads (find_thread_tid, (void *) &tid); + } + else + { + /* swapper, lwp gives the core, tid = 0 and is not unique. */ + tp = iterate_over_threads (find_thread_swapper, (void *) &lwp); + } + + ps = (linux_kthread_info_t *)tp->priv; + + if (debug_linuxkthread_threads > 2) + fprintf_unfiltered (gdb_stdlog, "ptid %s tp=0x%p ps=0x%p\n", + ptid_to_str(ptid), tp, tp->priv); + + /* Prune the gdb-thread if the process is not valid + meaning it was no longer found in the task list. */ + return ps; +} + +/* Iterate_over_threads() callback. Invalidate the gdb thread if + the linux process has died. */ + +static int +thread_clear_info (struct thread_info *tp, void *ignored) +{ + tp->priv = NULL; + return 0; +} + +/* Invalidate the cached Linux task list. */ + +static void +lkthread_invalidate_threadlist (void) +{ + linux_kthread_info_t *ps = lkthread_h->process_list; + linux_kthread_info_t *cur; + + while (ps) + { + cur = ps; + ps = ps->next; + cur->valid = 0; + } + + /* We invalidate the processes attached to the gdb_thread + setting tp->private to null tells if the thread can + be deleted or not. */ + + iterate_over_threads (thread_clear_info, NULL); + + kthread_list_invalid = TRUE; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "kthread_list_invalid (%d)\n", + kthread_list_invalid); +} + +/* Free memory allocated in the task list. */ + +static void +lkthread_free_threadlist (void) +{ + linux_kthread_info_t *ps = lkthread_h->process_list; + linux_kthread_info_t *cur; + while (ps) + { + cur = ps; + ps = ps->next; + xfree (cur->comm); + xfree (cur); + } + lkthread_h->process_list = NULL; +} + +/* Target Layer Implementation */ + + +/* If OBJFILE contains the symbols corresponding to the Linux kernel, + activate the thread stratum implemented by this module. */ + +static int +linux_kthread_activate (struct objfile *objfile) +{ + struct gdbarch *gdbarch = target_gdbarch (); + struct linux_kthread_arch_ops *arch_ops = gdbarch_linux_kthread_ops (gdbarch); + struct regcache *regcache; + CORE_ADDR pc; + + /* Skip if the thread stratum has already been activated. */ + if (linux_kthread_active) + return 0; + + /* There's no point in enabling this module if no + architecture-specific operations are provided. */ + if (!arch_ops) + return 0; + + /* Allocate global data struct. */ + lkthread_h = XCNEW (struct linux_kthread_data); + + /* Allocate private scratch buffer. */ + lkthread_h->scratch_buf_size = 4096; + lkthread_h->scratch_buf = + (unsigned char *) xcalloc (lkthread_h->scratch_buf_size, sizeof (char)); + + /* Verify that this represents an appropriate linux target. */ + + /* Check target halted at a kernel address, otherwise we can't + access any kernel memory. Using regcache_read_pc() is OK + here as we haven't pushed linux-kthread stratum yet. */ + regcache = get_thread_regcache (inferior_ptid); + pc = regcache_read_pc (regcache); + if (!arch_ops->is_kernel_address(pc)) + { + fprintf_unfiltered (gdb_stdlog, "linux_kthread_activate() target" + " stopped in user space\n"); + return 0; + } + + lkthread_init (); + + /* TODO: check kernel in memory matches vmlinux (Linux banner etc?) */ + + /* To get correct thread names from add_thread_with_info() + target_ops must be pushed before enumerating kthreads. */ + + push_target (linux_kthread_ops); + linux_kthread_active = 1; + + /* Scan the linux threads. */ + if (!lkthread_refresh_threadlist (stop_core)) + { + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "lkthread_refresh_threadlist\n"); + + /* Don't activate linux-kthread as no threads were found. */ + lkthread_invalidate_threadlist (); + + prune_threads(); + return 0; + } + + return 1; +} + +/* The linux-kthread to_close target_ops method. */ + +static void +linux_kthread_close (struct target_ops *self) +{ + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_close\n"); + +} + +/* Deactivate the linux-kthread stratum implemented by this module. */ + +static void +linux_kthread_deactivate (void) +{ + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_deactivate (%d)\n", + linux_kthread_active); + + /* Skip if the thread stratum has already been deactivated. */ + if (!linux_kthread_active) + return; + + lkthread_h->wait_process = NULL; + + lkthread_invalidate_threadlist(); + + lkthread_free_threadlist (); + + /* Reset collected symbol info. */ + lkthread_reset_fields_and_addrs (); + + /* Fallback to any thread that makes sense for the beneath target. */ + unpush_target (linux_kthread_ops); + + /* So we are only left with physical CPU threads from beneath + target. */ + prune_threads(); + + lkthread_free_percpu_data(max_cores); + + /* Free global lkthread struct. */ + xfree(lkthread_h); + + linux_kthread_active = 0; +} + +static void +linux_kthread_inferior_created (struct target_ops *ops, int from_tty) +{ + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_inferior_created\n"); + + linux_kthread_activate (NULL); +} + +/* The linux-kthread to_mourn_inferior target_ops method */ + +static void +linux_kthread_mourn_inferior (struct target_ops *ops) +{ + struct target_ops *beneath = find_target_beneath (ops); + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_mourn_inferior\n"); + beneath->to_mourn_inferior (beneath); + linux_kthread_deactivate (); +} + +/* The linux-kthread to_fetch_registers target_ops method. + This function determines whether the thread is running on + a physical CPU in which cases it defers to the layer beneath + to populate the register cache or if it is a sleeping + descheduled thread it uses the arch_ops to populate the registers + from what the kernel saved on the stack. */ + +static void +linux_kthread_fetch_registers (struct target_ops *ops, + struct regcache *regcache, int regnum) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct linux_kthread_arch_ops *arch_ops = gdbarch_linux_kthread_ops (gdbarch); + struct target_ops *beneath = find_target_beneath (ops); + CORE_ADDR addr = ptid_get_tid (inferior_ptid); + linux_kthread_info_t *ps; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_fetch_registers\n"); + + if (!(ps = lkthread_get_by_ptid (inferior_ptid)) + || lkthread_is_curr_task (ps)) + return beneath->to_fetch_registers (beneath, regcache, regnum); + + /* Call the platform specific code. */ + arch_ops->to_fetch_registers(regcache, regnum, ps->task_struct); +} + +/* The linux-kthread to_store_registers target_ops method. + This function determines whether the thread is running on + a physical CPU in which cases it defers to the layer beneath + or uses the arch_ops callback to write the registers into + the stack of the sleeping thread. */ + +static void +linux_kthread_store_registers (struct target_ops *ops, + struct regcache *regcache, int regnum) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct linux_kthread_arch_ops *arch_ops = gdbarch_linux_kthread_ops (gdbarch); + struct target_ops *beneath = find_target_beneath (ops); + linux_kthread_info_t *ps; + + if (debug_linuxkthread_threads) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_store_registers\n"); + + if (!(ps = lkthread_get_by_ptid (inferior_ptid)) || lkthread_is_curr_task (ps)) + return beneath->to_store_registers (beneath, regcache, regnum); + + /* Call the platform specific code. */ + arch_ops->to_store_registers(regcache, regnum, ps->task_struct); +} + +/* Helper function to always use layer beneath to fetch PC. + Parts of linux-kthread can't use regcache_read_pc() API to determine + the PC as it vectors through linux_kthread_fetch_registers() + which itself needs to read kernel memory to determine whether + the thread is sleeping or not. This function is used to help + determine whether the target stopped in userspace and therefore + linux-kthread can no longer read kernel memory or display + kernel threads. */ + +static CORE_ADDR lkthread_get_pc(struct target_ops *ops) +{ + struct gdbarch *gdbarch = target_gdbarch (); + struct target_ops *beneath = find_target_beneath (ops); + struct regcache *regcache; + CORE_ADDR pc; + int regnum; + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "lkthread_get_pc\n"); + + regcache = get_thread_regcache (inferior_ptid); + regnum = gdbarch_pc_regnum (gdbarch); + + gdb_assert(regnum > 0); + + beneath->to_fetch_registers (beneath, regcache, regnum); + + regcache_raw_collect (regcache, regnum, &pc); + + return pc; +} + +/* The linux-kthread to_wait target_ops method */ + +static ptid_t +linux_kthread_wait (struct target_ops *ops, + ptid_t ptid, struct target_waitstatus *status, + int options) +{ + struct gdbarch *gdbarch = target_gdbarch (); + struct linux_kthread_arch_ops *arch_ops = gdbarch_linux_kthread_ops (gdbarch); + struct target_ops *beneath = find_target_beneath (ops); + ptid_t stop_ptid; + CORE_ADDR pc; + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_wait\n"); + + /* Pass the request to the layer beneath. */ + stop_ptid = beneath->to_wait (beneath, ptid, status, options); + + /* get PC of CPU. */ + pc = lkthread_get_pc(ops); + + /* Check it is executing in the kernel before accessing kernel + memory. */ + if (!arch_ops->is_kernel_address(pc)) + { + fprintf_unfiltered (gdb_stdlog, "linux_kthread_wait() target stopped" + " in user space. Disabling linux-kthread\n"); + linux_kthread_deactivate(); + return stop_ptid; + } + + if (max_cores > 1) + stop_core = ptid_get_lwp (stop_ptid) - 1; + else + stop_core = 0; + + /* Reset the inferior_ptid to the stopped ptid. */ + inferior_ptid = stop_ptid; + + /* Rescan for new task, but avoid storming the debug connection. */ + lkthread_refresh_threadlist (stop_core); + + /* The above calls might will end up accessing the registers + of the target because of inhibit_thread_awareness(). However, + this will populate a register cache associated with + inferior_ptid, which we haven't updated yet. Force a flush + of these cached values so that they end up associated to + the right context. */ + registers_changed (); + + /* This is normally done by infrun.c:handle_inferior_event (), + but we need it set to access the frames for some operations + below (eg. in check_exec_actions (), where we don't know + what the user will ask in his commands. */ + set_executing (minus_one_ptid, 0); + + if (lkthread_h->wait_process) + { + inferior_ptid = PTID_OF (lkthread_h->wait_process); + stop_ptid = inferior_ptid; + } + + return stop_ptid; +} + +/* The linux-kthread to_resume target_ops method. */ + +static void +linux_kthread_resume (struct target_ops *ops, + ptid_t ptid, int step, enum gdb_signal sig) +{ + /* Pass the request to the layer beneath. */ + struct target_ops *beneath = find_target_beneath (ops); + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "Resuming %i with sig %i (step %i)\n", + (int) ptid_get_pid (ptid), (int) sig, step); + + beneath->to_resume (beneath, ptid, step, sig); +} + +/* The linux-kthread to_thread_alive target_ops method. */ + +static int +linux_kthread_thread_alive (struct target_ops *ops, ptid_t ptid) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + struct target_ops *beneath = find_target_beneath (ops); + linux_kthread_info_t *ps; + + if (debug_linuxkthread_targetops > 2) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_thread_alive ptid=%s\n", + ptid_to_str(ptid)); + + ps = lkthread_get_by_ptid (ptid); + + if (!ps) + { + if (debug_linuxkthread_threads > 2) + fprintf_unfiltered (gdb_stdlog, "Prune thread ps(%p)\n",ps); + + return 0; + } + + if (debug_linuxkthread_threads > 2) + fprintf_unfiltered (gdb_stdlog, "Alive thread ps(%p)\n",ps); + + return 1; +} + +/* The linux-kthread to_update_thread_list target_ops method. */ + +static void +linux_kthread_update_thread_list (struct target_ops *ops) +{ + struct target_ops *beneath = find_target_beneath (ops); + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_update_thread_list\n"); + + /* Build linux threads on top. */ + lkthread_get_threadlist (); + + prune_threads (); +} + +/* The linux-kthread to_extra_thread_info target_ops method. + Return a string describing the state of the thread specified by + INFO. */ + +static char * +linux_kthread_extra_thread_info (struct target_ops *self, + struct thread_info *info) +{ + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + linux_kthread_info_t *ps = (linux_kthread_info_t *) info->priv; + + if (ps) + { + char *msg = get_print_cell (); + size_t len = 0; + + len = snprintf (msg, PRINT_CELL_SIZE, "pid: %li tgid: %i", + ptid_get_tid(PTID_OF (ps)), ps->tgid); + + /* Now GDB is displaying all kernel threads it is important + to let the user know which threads are actually scheduled + on the CPU cores. We do this by adding to the + thread name if it is currently executing on the processor + when the target was halted. */ + + if (lkthread_is_curr_task (ps)) + snprintf (msg + len, PRINT_CELL_SIZE - len, " ", ps->core); + + return msg; + } + + return "LinuxThread"; +} + +/* The linux-kthread to_pid_to_str target_ops method. */ + +static char * +linux_kthread_pid_to_str (struct target_ops *ops, ptid_t ptid) +{ + linux_kthread_info_t *ps; + struct thread_info *tp; + + /* when quitting typically */ + if (!ptid_get_lwp(ptid)) + return "Linux Kernel"; + + tp = find_thread_ptid (ptid); + + if (!tp || !tp->priv) { + warning ("Suspicious !tp or !tp->priv"); + return ""; + } + + /* We use thread_info priv field for storing linux_kthread_info_t. */ + ps = (linux_kthread_info_t *) tp->priv; + + gdb_assert (ps->comm); + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "kthread_pid_to_str ptid %s str=%s\n", + ptid_to_str(ptid), ps->comm); + + return ps->comm; +} + +/* The linux-kthread to_thread_name target_ops method. */ + +static const char * +linux_kthread_thread_name (struct target_ops *ops, struct thread_info *thread) +{ + /* All the thread name information has generally been + returned already through the pid_to_str. + We could refactor this around and 'correct' the naming + but then you wouldn't get niceties such as + [Switching to thread 52 (getty)]. */ + + return NULL; +} + +/* The linux-kthread to_can_async_p target_ops method. */ + +static int +linux_kthread_can_async_p (struct target_ops *ops) +{ + return 0; +} + +/* The linux-kthread is_async_p target_ops method. */ + +static int +linux_kthread_is_async_p (struct target_ops *ops) +{ + return 0; +} + +/* The linux-kthread to_interrupt target_ops method. */ + +static void +linux_kthread_interrupt (struct target_ops *ops, ptid_t ptid) +{ + struct target_ops *beneath = find_target_beneath (ops); + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "linux_kthread_interrupt called\n"); + + if (!lkthread_disable_to_interrupt) + beneath->to_interrupt(ops, ptid); +} + +static struct target_ops * +linux_kthread_target (void) +{ + struct target_ops *t = XCNEW (struct target_ops); + + t->to_shortname = "linux-kthreads"; + t->to_longname = "linux kernel-level threads"; + t->to_doc = "Linux kernel-level threads"; + t->to_close = linux_kthread_close; + t->to_mourn_inferior = linux_kthread_mourn_inferior; + /* Registers */ + t->to_fetch_registers = linux_kthread_fetch_registers; + t->to_store_registers = linux_kthread_store_registers; + + /* Execution */ + t->to_wait = linux_kthread_wait; + t->to_resume = linux_kthread_resume; + + /* Threads */ + t->to_thread_alive = linux_kthread_thread_alive; + t->to_update_thread_list = linux_kthread_update_thread_list; + t->to_extra_thread_info = linux_kthread_extra_thread_info; + t->to_thread_name = linux_kthread_thread_name; + t->to_pid_to_str = linux_kthread_pid_to_str; + t->to_stratum = thread_stratum; + t->to_magic = OPS_MAGIC; + + t->to_interrupt = linux_kthread_interrupt; + + linux_kthread_ops = t; + + /* Prevent async operations */ + t->to_can_async_p = linux_kthread_can_async_p; + t->to_is_async_p = linux_kthread_is_async_p; + + return t; +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ +extern initialize_file_ftype _initialize_linux_kthread; + +/* Command-list for the "set/show linuxkthread" prefix command. */ +static struct cmd_list_element *set_linuxkthread_list; +static struct cmd_list_element *show_linuxkthread_list; + +static void +set_linuxkthread_command (char *arg, int from_tty) +{ + printf_unfiltered (_(\ +"\"set linuxkthread\" must be followed by the name of a setting.\n")); + help_list (set_linuxkthread_list, "set linuxkthread ", all_commands, gdb_stdout); +} + +/* Implement the "show linuxkthread" prefix command. */ + +static void +show_linuxkthread_command (char *args, int from_tty) +{ + cmd_show_list (show_linuxkthread_list, from_tty, ""); +} + +/* This function is called after load, or after attach, when we know + that the kernel code is in memory. (This might be called direclty + by the user by issuing 'set linux-kthread loaded on', if he doesn't + use a standard attach mechanism. */ + +void +lkthread_loaded_set (char *arg, int from_tty, struct cmd_list_element *c) +{ + ptid_t stop_ptid; + + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "lkthread_loaded_set (%d)\n", + linux_kthread_loaded); + + /* If stratum already active, and user requests it to be disabled. */ + if (linux_kthread_active && !linux_kthread_loaded) + { + linux_kthread_deactivate (); + } + else if (!linux_kthread_active && linux_kthread_loaded) + { + /* If already disabled, and user requests it to be enabled. */ + stop_core = 0; + linux_kthread_activate (NULL); + } +} + + +void +_initialize_linux_kthread (void) +{ + if (debug_linuxkthread_targetops) + fprintf_unfiltered (gdb_stdlog, "_initialize_linux_kthread\n"); + + complete_target_initialization (linux_kthread_target ()); + + /* Notice when a inferior is created in order to push the + linuxkthread ops if needed. */ + observer_attach_inferior_created (linux_kthread_inferior_created); + + add_prefix_cmd ("linuxkthread", no_class, set_linuxkthread_command, + _("Prefix command for changing Linuxkthread-specific settings"), + &set_linuxkthread_list, "set linuxkthread ", 0, &setlist); + + add_prefix_cmd ("linuxkthread", no_class, show_linuxkthread_command, + _("Prefix command for showing Linuxkthread-specific settings"), + &show_linuxkthread_list, "show linuxkthread ", 0, &showlist); + + add_setshow_boolean_cmd ("loaded", + no_class, + &linux_kthread_loaded, + "Enable support for Linux thread runtime", + "Disable support for Linux thread runtime", + NULL, &lkthread_loaded_set, NULL, + &set_linuxkthread_list, + &show_linuxkthread_list); + +} diff --git a/gdb/linux-kthread.h b/gdb/linux-kthread.h new file mode 100644 index 0000000..cffa0f4 --- /dev/null +++ b/gdb/linux-kthread.h @@ -0,0 +1,223 @@ +/* Linux kernel-level threads support. + + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef LINUX_KTHREAD_H +#define LINUX_KTHREAD_H 1 + +#include "objfiles.h" + +struct addr_info +{ + char *name; + struct bound_minimal_symbol bmsym; + /* Chained to allow easy cleanup. */ + struct addr_info *next; +}; + +struct field_info +{ + char *struct_name; + char *field_name; + struct symbol *type; + int offset; + int size; + /* Chained to allow easy cleanup. */ + struct field_info *next; +}; + + +/* The list of Linux threads cached by linux-kthread. */ +typedef struct private_thread_info +{ + struct private_thread_info *next; + CORE_ADDR task_struct; + CORE_ADDR mm; + CORE_ADDR active_mm; + + ptid_t old_ptid; + + /* This is the "dynamic" core info. */ + int core; + + int tgid; + unsigned int prio; + char *comm; + int valid; + + struct thread_info *gdb_thread; +} linux_kthread_info_t; + +#define PTID_OF(ps) ((ps)->gdb_thread->ptid) + +int lkthread_lookup_addr (struct addr_info *field, int check); +int lkthread_lookup_field (struct field_info *field, int check); + +static inline CORE_ADDR +lkthread_get_address (struct addr_info *addr) +{ + if (addr->bmsym.minsym == NULL) + lkthread_lookup_addr (addr, 0); + + return BMSYMBOL_VALUE_ADDRESS (addr->bmsym); +} + +static inline unsigned int +lkthread_get_field_offset (struct field_info *field) +{ + if (field->type == NULL) + lkthread_lookup_field (field, 0); + + return field->offset; +} + +static inline unsigned int +lkthread_get_field_size (struct field_info *field) +{ + if (field->type == NULL) + lkthread_lookup_field (field, 0); + + return field->size; +} + +#define CORE_INVAL (-1) + +#define FIELD_INFO(s_name, field) _FIELD_##s_name##__##field + +#define DECLARE_FIELD(s_name, field) \ + static struct field_info FIELD_INFO(s_name, field) \ + = { .struct_name = #s_name, .field_name = #field, 0 } + +#define F_OFFSET(struct, field) \ + lkthread_get_field_offset (&FIELD_INFO(struct, field)) + +#define F_SIZE(struct, field) \ + lkthread_get_field_size (&FIELD_INFO(struct, field)) + +#define HAS_FIELD(struct, field) \ + (FIELD_INFO(struct, field).type != NULL \ + || (lkthread_lookup_field(&FIELD_INFO(struct, field), 1), \ + FIELD_INFO(struct, field).type != NULL)) + +#define DECLARE_ADDR(symb) \ + static struct addr_info symb = { .name = #symb, .bmsym = {NULL, NULL} } + +#define HAS_ADDR(symb) \ + (symb.bmsym.minsym != NULL \ + || (lkthread_lookup_addr(&symb, 1), symb.bmsym.minsym != NULL)) + +#define HAS_ADDR_PTR(symb) \ + (symb->bmsym.minsym != NULL \ + || (lkthread_lookup_addr(symb, 1), symb->bmsym.minsym != NULL)) + +#define ADDR(sym) lkthread_get_address (&sym) + +#define ADDR_PTR(sym) lkthread_get_address (sym) + +#define read_unsigned_field(base, struct, field, byteorder) \ + read_memory_unsigned_integer (base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), byteorder) + +#define read_signed_field(base, struct, field, byteorder) \ + read_memory_integer (base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), byteorder) + +#define read_pointer_field(base, struct, field) \ + read_memory_typed_address (base + F_OFFSET (struct, field), \ + builtin_type (target_gdbarch ())->builtin_data_ptr) + +#define read_unsigned_embedded_field(base, struct, field, emb_str, \ + emb_field, byteorder) \ + read_memory_unsigned_integer (base + F_OFFSET (struct, field) \ + + F_OFFSET (emb_str, emb_field), \ + F_SIZE (emb_str, emb_field), byteorder) + +#define read_signed_embedded_field(base, struct, field, emb_str, \ + emb_field, byteorder) \ + read_memory_integer (base + F_OFFSET (struct, field) \ + + F_OFFSET (emb_str, emb_field), \ + F_SIZE (emb_str, emb_field), byteorder) + +#define read_pointer_embedded_field(base, struct, field, emb_str, \ + emb_field) \ + read_memory_typed_address (base + F_OFFSET (struct, field) \ + + F_OFFSET (emb_str, emb_field), \ + builtin_type (target_gdbarch ())->builtin_data_ptr) + +#define extract_unsigned_field(base, struct, field, byteorder) \ + extract_unsigned_integer(base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), byteorder) + +#define extract_signed_field(base, struct, field, byteorder) \ + extract_signed_integer (base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), byteorder) + +#define extract_pointer_field(base, struct, field) \ + extract_typed_address (base + F_OFFSET (struct, field), \ + builtin_type(target_gdbarch ())->builtin_data_ptr) + +/* Mimic kernel macros. */ +#define container_of(ptr, struc, field) ((ptr) - F_OFFSET(struc, field)) + + +/* Mapping GDB PTID to Linux PID and Core + + GDB Remote uses LWP to store the effective cpu core + ptid.pid = Inferior PID + ptid.lwp = CPU Core + ptid.tid = 0 + + We store Linux PID in TID. */ + +/* Architecture-specific hooks. */ + +struct linux_kthread_arch_ops +{ + void (*to_fetch_registers) (struct regcache *regcache, int regnum, + CORE_ADDR task_struct); + + void (*to_store_registers) (const struct regcache *regcache, int regnum, + CORE_ADDR addr); + + int (*is_kernel_address) (const CORE_ADDR addr); +}; + +/* Whether target_ops to_interrupt is disabled */ +extern int lkthread_disable_to_interrupt; + +/* Set the function that supplies registers for an inactive thread for + architecture GDBARCH to SUPPLY_KTHREAD. */ + +extern void linux_kthread_set_supply_thread (struct gdbarch *gdbarch, + void (*supply_kthread) (struct regcache *, + int, CORE_ADDR)); + + +/* Set the function that collects registers for an inactive thread for + architecture GDBARCH to SUPPLY_KTHREAD. */ + +extern void linux_kthread_set_collect_thread (struct gdbarch *gdbarch, + void (*collect_kthread) (const struct regcache *, + int, CORE_ADDR)); + +/* Return the macro replacement string for a given macro at a particular + symbol location. */ +const char * kthread_find_macro_at_symbol(struct addr_info *symbol, char *name); + + +#endif /* linux_kthread.h */