Index: lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h =================================================================== --- lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h +++ lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h @@ -46,6 +46,8 @@ protected: bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; + + lldb::addr_t FindSymbol(const char* name); }; #endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H Index: lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp =================================================================== --- lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp +++ lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp @@ -137,12 +137,115 @@ return false; } - const Symbol *pcb_sym = - GetTarget().GetExecutableModule()->FindFirstSymbolWithNameAndType( - ConstString("dumppcb")); - ThreadSP thread_sp(new ThreadFreeBSDKernel( - *this, 1, pcb_sym ? pcb_sym->GetFileAddress() : LLDB_INVALID_ADDRESS)); - new_thread_list.AddThread(thread_sp); + Status error; + + // struct field offsets are written as symbols so that we don't have + // to figure them out ourselves + int32_t offset_p_list = ReadSignedIntegerFromMemory( + FindSymbol("proc_off_p_list"), 4, -1, error); + int32_t offset_p_pid = + ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error); + int32_t offset_p_threads = ReadSignedIntegerFromMemory( + FindSymbol("proc_off_p_threads"), 4, -1, error); + int32_t offset_p_comm = ReadSignedIntegerFromMemory( + FindSymbol("proc_off_p_comm"), 4, -1, error); + + int32_t offset_td_tid = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_tid"), 4, -1, error); + int32_t offset_td_plist = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_plist"), 4, -1, error); + int32_t offset_td_pcb = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_pcb"), 4, -1, error); + int32_t offset_td_oncpu = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_oncpu"), 4, -1, error); + int32_t offset_td_name = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_name"), 4, -1, error); + + // fail if we were not able to read any of the offsets + if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 || + offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 || + offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1) + return false; + + // dumptid contains the thread-id of the crashing thread + // dumppcb contains its PCB + int32_t dumptid = + ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error); + lldb::addr_t dumppcb = FindSymbol("dumppcb"); + + // stoppcbs is an array of PCBs on all CPUs + // each element is of size pcb_size + int32_t pcbsize = + ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error); + lldb::addr_t stoppcbs = FindSymbol("stoppcbs"); + + // from FreeBSD sys/param.h + constexpr size_t fbsd_maxcomlen = 19; + + // iterate through a linked list of all processes + // allproc is a pointer to the first list element, p_list field + // (found at offset_p_list) specifies the next element + for (lldb::addr_t proc = + ReadPointerFromMemory(FindSymbol("allproc"), error); + proc != 0 && proc != LLDB_INVALID_ADDRESS; + proc = ReadPointerFromMemory(proc + offset_p_list, error)) { + int32_t pid = + ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error); + // process' command-line string + char comm[fbsd_maxcomlen + 1]; + ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error); + + // iterate through a linked list of all process' threads + // the initial thread is found in process' p_threads, subsequent + // elements are linked via td_plist field + for (lldb::addr_t td = + ReadPointerFromMemory(proc + offset_p_threads, error); + td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) { + int32_t tid = + ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error); + lldb::addr_t pcb_addr = + ReadPointerFromMemory(td + offset_td_pcb, error); + // whether process was on CPU (-1 if not, otherwise CPU number) + int32_t oncpu = + ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error); + // thread name + char thread_name[fbsd_maxcomlen + 1]; + ReadCStringFromMemory(td + offset_td_name, thread_name, + sizeof(thread_name), error); + + // if we failed to read TID, ignore this thread + if (tid == -1) + continue; + + std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm); + if (*thread_name && strcmp(thread_name, comm)) { + thread_desc += '/'; + thread_desc += thread_name; + } + + // roughly: + // 1. if the thread crashed, its PCB is going to be at "dumppcb" + // 2. if the thread was on CPU, its PCB is going to be on the CPU + // 3. otherwise, its PCB is in the thread struct + if (tid == dumptid) { + // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed + pcb_addr = dumppcb; + thread_desc += " (crashed)"; + } else if (oncpu != -1) { + // if we managed to read stoppcbs and pcb_size, use them to find + // the correct PCB + if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0) + pcb_addr = stoppcbs + oncpu * pcbsize; + else + pcb_addr = LLDB_INVALID_ADDRESS; + thread_desc += llvm::formatv(" (on CPU {0})", oncpu); + } + + ThreadSP thread_sp{ + new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)}; + new_thread_list.AddThread(thread_sp); + } + } } else { const uint32_t num_threads = old_thread_list.GetSize(false); for (uint32_t i = 0; i < num_threads; ++i) @@ -163,6 +266,12 @@ return m_dyld_up.get(); } +lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) { + ModuleSP mod_sp = GetTarget().GetExecutableModule(); + const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name)); + return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS; +} + #if LLDB_ENABLE_FBSDVMCORE ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, Index: lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.h =================================================================== --- lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.h +++ lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.h @@ -14,7 +14,7 @@ class ThreadFreeBSDKernel : public lldb_private::Thread { public: ThreadFreeBSDKernel(lldb_private::Process &process, lldb::tid_t tid, - lldb::addr_t pcb_addr); + lldb::addr_t pcb_addr, std::string thread_name); ~ThreadFreeBSDKernel() override; @@ -25,10 +25,24 @@ lldb::RegisterContextSP CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + const char *GetName() override { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); + } + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + protected: bool CalculateStopInfo() override; private: + std::string m_thread_name; lldb::RegisterContextSP m_thread_reg_ctx_sp; lldb::addr_t m_pcb_addr; }; Index: lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.cpp =================================================================== --- lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.cpp +++ lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.cpp @@ -24,8 +24,10 @@ using namespace lldb_private; ThreadFreeBSDKernel::ThreadFreeBSDKernel(Process &process, lldb::tid_t tid, - lldb::addr_t pcb_addr) - : Thread(process, tid), m_pcb_addr(pcb_addr) {} + lldb::addr_t pcb_addr, + std::string thread_name) + : Thread(process, tid), m_thread_name(std::move(thread_name)), + m_pcb_addr(pcb_addr) {} ThreadFreeBSDKernel::~ThreadFreeBSDKernel() {} @@ -61,9 +63,8 @@ m_pcb_addr); break; case llvm::Triple::x86: - m_thread_reg_ctx_sp = - std::make_shared( - *this, new RegisterContextFreeBSD_i386(arch), m_pcb_addr); + m_thread_reg_ctx_sp = std::make_shared( + *this, new RegisterContextFreeBSD_i386(arch), m_pcb_addr); break; case llvm::Triple::x86_64: m_thread_reg_ctx_sp = Index: lldb/test/API/functionalities/postmortem/FreeBSDKernel/TestFreeBSDKernelVMCore.py =================================================================== --- lldb/test/API/functionalities/postmortem/FreeBSDKernel/TestFreeBSDKernelVMCore.py +++ lldb/test/API/functionalities/postmortem/FreeBSDKernel/TestFreeBSDKernelVMCore.py @@ -28,42 +28,48 @@ shutil.copyfileobj(inf, outf) return dest - def do_test(self, kernel_yaml, vmcore_bz2, bt_expected, regs_expected, - hz_value=100): + def do_test(self, kernel_yaml, vmcore_bz2, numthread, threads={}, hz=100): target = self.make_target(kernel_yaml) vmcore_file = self.make_vmcore(vmcore_bz2) process = target.LoadCore(vmcore_file) self.assertTrue(process, PROCESS_IS_VALID) - self.assertEqual(process.GetNumThreads(), 1) + self.assertEqual(process.GetNumThreads(), numthread) self.assertEqual(process.GetProcessID(), 0) # test memory reading self.expect("expr -- *(int *) &hz", - substrs=["(int) $0 = %d" % hz_value]) + substrs=["(int) $0 = %d" % hz]) main_mod = target.GetModuleAtIndex(0) hz_addr = (main_mod.FindSymbols("hz")[0].symbol.addr .GetLoadAddress(target)) error = lldb.SBError() self.assertEqual(process.ReadMemory(hz_addr, 4, error), - struct.pack("pmfd = open(mf, O_RDONLY | O_CLOEXEC, 0)) < 0) { ++ if ((kd->pmfd = open(mf, O_RDWR | O_CLOEXEC, 0)) < 0) { + _fvc_syserr(kd, kd->program, "%s", mf); + goto failed; + } +@@ -297,6 +297,47 @@ fvc_read(fvc_t *kd, fvc_addr_t kva, void *buf, size_t len) _fvc_syserr(kd, kd->program, "fvc_read"); break; } + printf("%% RD: %zu %d\n", pa, cc); ++ /* ++ * If ka_kvatop returns a bogus value or our core file is ++ * truncated, we might wind up seeking beyond the end of the ++ * core file in which case the read will return 0 (EOF). ++ */ ++ if (cr == 0) ++ break; ++ cp += cr; ++ kva += cr; ++ len -= cr; ++ } ++ ++ return (cp - (char *)buf); ++} ++ ++ssize_t ++fvc_write(fvc_t *kd, fvc_addr_t kva, const void *buf, size_t len) ++{ ++ int cc; ++ ssize_t cr; ++ off_t pa; ++ const char *cp; ++ ++ cp = buf; ++ while (len > 0) { ++ cc = kd->arch->ka_kvatop(kd, kva, &pa); ++ if (cc == 0) ++ return (-1); ++ if (cc > (ssize_t)len) ++ cc = len; ++ errno = 0; ++ if (lseek(kd->pmfd, pa, 0) == -1 && errno != 0) { ++ _fvc_syserr(kd, 0, _PATH_MEM); ++ break; ++ } ++ cr = write(kd->pmfd, cp, cc); ++ if (cr < 0) { ++ _fvc_syserr(kd, kd->program, "fvc_write"); ++ break; ++ } /* * If ka_kvatop returns a bogus value or our core file is * truncated, we might wind up seeking beyond the end of the -@@ -331,3 +332,8 @@ fvc_kerndisp(fvc_t *kd) +@@ -331,3 +372,8 @@ fvc_kerndisp(fvc_t *kd) return (kd->arch->ka_kerndisp(kd)); } @@ -20,7 +81,7 @@ + return pread(fd, buf, count, offset); +} diff --git a/lib/fvc.h b/lib/fvc.h -index 8680079..ff1e0f0 100644 +index 8680079..8cff17c 100644 --- a/lib/fvc.h +++ b/lib/fvc.h @@ -54,6 +54,8 @@ typedef unsigned char fvc_vm_prot_t; @@ -32,6 +93,14 @@ struct fvc_page { unsigned int kp_version; fvc_addr_t kp_paddr; +@@ -76,6 +78,7 @@ fvc_t *fvc_open + (const char *, const char *, char *, + int (*)(const char *, fvc_addr_t *, void *), void *); + ssize_t fvc_read(fvc_t *, fvc_addr_t, void *, size_t); ++ssize_t fvc_write(fvc_t *, fvc_addr_t, const void *, size_t); + ssize_t fvc_kerndisp(fvc_t *); + + typedef int fvc_walk_pages_cb_t(struct fvc_page *, void *); diff --git a/lib/fvc_amd64.c b/lib/fvc_amd64.c index 4d27998..69f1807 100644 --- a/lib/fvc_amd64.c @@ -164,3 +233,91 @@ if (rd < 0 || rd != (ssize_t)map_len) { _fvc_err(kd, kd->program, "cannot read %zu bytes for bitmap", map_len); +diff --git a/man/fbsdvmcore.3 b/man/fbsdvmcore.3 +index 4285ba2..c0d760c 100644 +--- a/man/fbsdvmcore.3 ++++ b/man/fbsdvmcore.3 +@@ -89,4 +89,5 @@ etc. + .Xr fvc_geterr 3 , + .Xr fvc_kerndisp 3 , + .Xr fvc_open 3 , +-.Xr fvc_read 3 ++.Xr fvc_read 3 , ++.Xr fvc_write 3 +diff --git a/man/fvc_geterr.3 b/man/fvc_geterr.3 +index 964a097..7d74c25 100644 +--- a/man/fvc_geterr.3 ++++ b/man/fvc_geterr.3 +@@ -66,7 +66,8 @@ or an error has not been captured for + .Sh SEE ALSO + .Xr fvc 3 , + .Xr fvc_close 3 , +-.Xr fvc_read 3 ++.Xr fvc_read 3 , ++.Xr fvc_write 3 + .Sh BUGS + This routine cannot be used to access error conditions due to a failed + .Fn fvc_open +diff --git a/man/fvc_open.3 b/man/fvc_open.3 +index 1f8e3be..4ea93ed 100644 +--- a/man/fvc_open.3 ++++ b/man/fvc_open.3 +@@ -166,5 +166,6 @@ was + .Xr fvc_geterr 3 , + .Xr fvc_native 3 , + .Xr fvc_read 3 , ++.Xr fvc_write 3 , + .Xr kmem 4 , + .Xr mem 4 +diff --git a/man/fvc_read.3 b/man/fvc_read.3 +index 7413d59..c18dadc 100644 +--- a/man/fvc_read.3 ++++ b/man/fvc_read.3 +@@ -36,18 +36,24 @@ + .Dt FVC_READ 3 + .Os + .Sh NAME +-.Nm fvc_read +-.Nd read kernel virtual memory ++.Nm fvc_read , ++.Nm fvc_write ++.Nd read or write kernel virtual memory + .Sh LIBRARY + .Lb libfbsdvmcore + .Sh SYNOPSIS + .In fvc.h + .Ft ssize_t + .Fn fvc_read "fvc_t *kd" "kvaddr_t addr" "void *buf" "size_t nbytes" ++.Ft ssize_t ++.Fn fvc_write "fvc_t *kd" "kvaddr_t addr" "void *buf" "size_t nbytes" + .Sh DESCRIPTION + The + .Fn fvc_read + function is used to read a crash dump file. ++.Fn fvc_write ++function is used to overwrite parts of a crash dump file. ++Note that only the fragments already present can be written. + See + .Fn fvc_open 3 + for information regarding opening kernel crash dumps. +@@ -63,6 +69,13 @@ to + .Fa buf . + .Pp + The ++.Fn fvc_write ++function transfers ++.Fa nbytes ++bytes of data from ++.Fa buf ++to the kernel space address ++.Fa addr . + .Sh RETURN VALUES + Upon success, the number of bytes actually transferred is returned. + Otherwise, -1 is returned. +diff --git a/man/fvc_write.3 b/man/fvc_write.3 +new file mode 100644 +index 0000000..f25fc74 +--- /dev/null ++++ b/man/fvc_write.3 +@@ -0,0 +1 @@ ++.so man3/fvc_read.3 Index: lldb/test/API/functionalities/postmortem/FreeBSDKernel/tools/lldb-minimize-processes.patch =================================================================== --- /dev/null +++ lldb/test/API/functionalities/postmortem/FreeBSDKernel/tools/lldb-minimize-processes.patch @@ -0,0 +1,87 @@ +diff --git a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp +index aff3934df25b..eb6e39a33104 100644 +--- a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp ++++ b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp +@@ -38,6 +38,8 @@ public: + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; ++ size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, ++ size_t size, Status &error) override; + + private: + fvc_t *m_fvc; +@@ -185,6 +187,7 @@ bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, + // iterate through a linked list of all processes + // allproc is a pointer to the first list element, p_list field + // (found at offset_p_list) specifies the next element ++ lldb::addr_t prev = 0; + for (lldb::addr_t proc = + ReadPointerFromMemory(FindSymbol("allproc"), error); + proc != 0 && proc != LLDB_INVALID_ADDRESS; +@@ -195,6 +198,8 @@ bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, + char comm[fbsd_maxcomlen + 1]; + ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error); + ++ bool interesting = false; ++ + // iterate through a linked list of all process' threads + // the initial thread is found in process' p_threads, subsequent + // elements are linked via td_plist field +@@ -223,6 +228,7 @@ bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, + if (tid == dumptid) { + // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed + pcb_addr = dumppcb; ++ interesting = true; + } else if (oncpu != -1) { + // if we managed to read stoppcbs and pcb_size, use them to find + // the correct PCB +@@ -230,14 +236,28 @@ bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, + pcb_addr = stoppcbs + oncpu * pcbsize; + else + pcb_addr = LLDB_INVALID_ADDRESS; ++ interesting = true; + } + + ThreadSP thread_sp(new ThreadFreeBSDKernel( + *this, tid, pcb_addr, +- llvm::formatv("(pid {0}) {1}/{2}", pid, comm, thread_name))); ++ llvm::formatv("(pid {0}) {1}/{2} (oncpu = {3})", pid, comm, thread_name, oncpu))); + new_thread_list.AddThread(thread_sp); + } ++ ++ if (interesting) { ++ printf("pid %d is interesting\n", pid); ++ if (prev != 0) { ++ printf("will link %d to %d\n", prev, proc); ++ if (!WritePointerToMemory(prev + offset_p_list, proc, error)) ++ assert(0 && "write failed"); ++ } ++ prev = proc; ++ } + } ++ printf("last: %d\n", prev); ++ if (!WritePointerToMemory(prev + offset_p_list, 0, error)) ++ assert(0 && "write failed"); + } else { + const uint32_t num_threads = old_thread_list.GetSize(false); + for (uint32_t i = 0; i < num_threads; ++i) +@@ -287,6 +307,18 @@ size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf, + return rd; + } + ++size_t ProcessFreeBSDKernelFVC::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, ++ size_t size, Status &error) { ++ ssize_t rd = 0; ++ rd = fvc_write(m_fvc, vm_addr, buf, size); ++ printf("fvc_write(%p, %p, %d) -> %d\n", vm_addr, buf, size, rd); ++ if (rd < 0 || static_cast(rd) != size) { ++ error.SetErrorStringWithFormat("Writing memory failed: %s", GetError()); ++ return rd > 0 ? rd : 0; ++ } ++ return rd; ++} ++ + const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); } + + #endif // LLDB_ENABLE_FBSDVMCORE Index: lldb/test/API/functionalities/postmortem/FreeBSDKernel/tools/test.script =================================================================== --- lldb/test/API/functionalities/postmortem/FreeBSDKernel/tools/test.script +++ lldb/test/API/functionalities/postmortem/FreeBSDKernel/tools/test.script @@ -1,5 +1,11 @@ thread list -register read pc +register read --all +bt +thread select 2 +register read --all +bt +thread select 3 +register read --all bt p *(int*)&hz memory read &hz