1//===-- ThreadElfCore.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Target/RegisterContext.h"
10#include "lldb/Target/StopInfo.h"
11#include "lldb/Target/Target.h"
12#include "lldb/Target/UnixSignals.h"
13#include "lldb/Target/Unwind.h"
14#include "lldb/Utility/DataExtractor.h"
15#include "lldb/Utility/LLDBLog.h"
16#include "lldb/Utility/Log.h"
17#include "lldb/Utility/ProcessInfo.h"
18
19#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
20#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h"
21#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h"
22#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
23#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
24#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h"
25#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
26#include "Plugins/Process/Utility/RegisterContextNetBSD_i386.h"
27#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h"
28#include "Plugins/Process/Utility/RegisterContextOpenBSD_i386.h"
29#include "Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h"
30#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
31#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
32#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h"
33#include "ProcessElfCore.h"
34#include "RegisterContextLinuxCore_x86_64.h"
35#include "RegisterContextPOSIXCore_arm.h"
36#include "RegisterContextPOSIXCore_arm64.h"
37#include "RegisterContextPOSIXCore_loongarch64.h"
38#include "RegisterContextPOSIXCore_mips64.h"
39#include "RegisterContextPOSIXCore_powerpc.h"
40#include "RegisterContextPOSIXCore_ppc64le.h"
41#include "RegisterContextPOSIXCore_riscv32.h"
42#include "RegisterContextPOSIXCore_riscv64.h"
43#include "RegisterContextPOSIXCore_s390x.h"
44#include "RegisterContextPOSIXCore_x86_64.h"
45#include "ThreadElfCore.h"
46
47#include <memory>
48
49using namespace lldb;
50using namespace lldb_private;
51
52// Construct a Thread object with given data
53ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td)
54 : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(),
55 m_gpregset_data(td.gpregset), m_notes(td.notes),
56 m_siginfo_bytes(std::move(td.siginfo_bytes)), m_signo(td.signo) {}
57
58ThreadElfCore::~ThreadElfCore() { DestroyThread(); }
59
60void ThreadElfCore::RefreshStateAfterStop() {
61 GetRegisterContext()->InvalidateIfNeeded(force: false);
62}
63
64RegisterContextSP ThreadElfCore::GetRegisterContext() {
65 if (!m_reg_context_sp) {
66 m_reg_context_sp = CreateRegisterContextForFrame(frame: nullptr);
67 }
68 return m_reg_context_sp;
69}
70
71RegisterContextSP
72ThreadElfCore::CreateRegisterContextForFrame(StackFrame *frame) {
73 RegisterContextSP reg_ctx_sp;
74 uint32_t concrete_frame_idx = 0;
75 Log *log = GetLog(mask: LLDBLog::Thread);
76
77 if (frame)
78 concrete_frame_idx = frame->GetConcreteFrameIndex();
79
80 bool is_linux = false;
81 if (concrete_frame_idx == 0) {
82 if (m_thread_reg_ctx_sp)
83 return m_thread_reg_ctx_sp;
84
85 ProcessElfCore *process = static_cast<ProcessElfCore *>(GetProcess().get());
86 ArchSpec arch = process->GetArchitecture();
87 RegisterInfoInterface *reg_interface = nullptr;
88
89 switch (arch.GetTriple().getOS()) {
90 case llvm::Triple::FreeBSD: {
91 switch (arch.GetMachine()) {
92 case llvm::Triple::aarch64:
93 case llvm::Triple::arm:
94 break;
95 case llvm::Triple::ppc:
96 reg_interface = new RegisterContextFreeBSD_powerpc32(arch);
97 break;
98 case llvm::Triple::ppc64:
99 case llvm::Triple::ppc64le:
100 reg_interface = new RegisterContextFreeBSD_powerpc64(arch);
101 break;
102 case llvm::Triple::mips64:
103 reg_interface = new RegisterContextFreeBSD_mips64(arch);
104 break;
105 case llvm::Triple::x86:
106 reg_interface = new RegisterContextFreeBSD_i386(arch);
107 break;
108 case llvm::Triple::x86_64:
109 reg_interface = new RegisterContextFreeBSD_x86_64(arch);
110 break;
111 default:
112 break;
113 }
114 break;
115 }
116
117 case llvm::Triple::NetBSD: {
118 switch (arch.GetMachine()) {
119 case llvm::Triple::aarch64:
120 break;
121 case llvm::Triple::x86:
122 reg_interface = new RegisterContextNetBSD_i386(arch);
123 break;
124 case llvm::Triple::x86_64:
125 reg_interface = new RegisterContextNetBSD_x86_64(arch);
126 break;
127 default:
128 break;
129 }
130 break;
131 }
132
133 case llvm::Triple::Linux: {
134 is_linux = true;
135 switch (arch.GetMachine()) {
136 case llvm::Triple::aarch64:
137 break;
138 case llvm::Triple::ppc64le:
139 reg_interface = new RegisterInfoPOSIX_ppc64le(arch);
140 break;
141 case llvm::Triple::systemz:
142 reg_interface = new RegisterContextLinux_s390x(arch);
143 break;
144 case llvm::Triple::x86:
145 reg_interface = new RegisterContextLinux_i386(arch);
146 break;
147 case llvm::Triple::x86_64:
148 reg_interface = new RegisterContextLinux_x86_64(arch);
149 break;
150 default:
151 break;
152 }
153 break;
154 }
155
156 case llvm::Triple::OpenBSD: {
157 switch (arch.GetMachine()) {
158 case llvm::Triple::aarch64:
159 break;
160 case llvm::Triple::x86:
161 reg_interface = new RegisterContextOpenBSD_i386(arch);
162 break;
163 case llvm::Triple::x86_64:
164 reg_interface = new RegisterContextOpenBSD_x86_64(arch);
165 break;
166 default:
167 break;
168 }
169 break;
170 }
171
172 default:
173 break;
174 }
175
176 if (!reg_interface && arch.GetMachine() != llvm::Triple::aarch64 &&
177 arch.GetMachine() != llvm::Triple::arm &&
178 arch.GetMachine() != llvm::Triple::loongarch64 &&
179 arch.GetMachine() != llvm::Triple::riscv64 &&
180 arch.GetMachine() != llvm::Triple::riscv32) {
181 LLDB_LOGF(log, "elf-core::%s:: Architecture(%d) or OS(%d) not supported",
182 __FUNCTION__, arch.GetMachine(), arch.GetTriple().getOS());
183 assert(false && "Architecture or OS not supported");
184 }
185
186 switch (arch.GetMachine()) {
187 case llvm::Triple::aarch64:
188 m_thread_reg_ctx_sp = RegisterContextCorePOSIX_arm64::Create(
189 thread&: *this, arch, gpregset: m_gpregset_data, notes: m_notes);
190 break;
191 case llvm::Triple::arm:
192 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_arm>(
193 args&: *this, args: std::make_unique<RegisterInfoPOSIX_arm>(args&: arch), args&: m_gpregset_data,
194 args&: m_notes);
195 break;
196 case llvm::Triple::loongarch64:
197 m_thread_reg_ctx_sp = RegisterContextCorePOSIX_loongarch64::Create(
198 thread&: *this, arch, gpregset: m_gpregset_data, notes: m_notes);
199 break;
200 case llvm::Triple::riscv32:
201 m_thread_reg_ctx_sp = RegisterContextCorePOSIX_riscv32::Create(
202 thread&: *this, arch, gpregset: m_gpregset_data, notes: m_notes);
203 break;
204 case llvm::Triple::riscv64:
205 m_thread_reg_ctx_sp = RegisterContextCorePOSIX_riscv64::Create(
206 thread&: *this, arch, gpregset: m_gpregset_data, notes: m_notes);
207 break;
208 case llvm::Triple::mipsel:
209 case llvm::Triple::mips:
210 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>(
211 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
212 break;
213 case llvm::Triple::mips64:
214 case llvm::Triple::mips64el:
215 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>(
216 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
217 break;
218 case llvm::Triple::ppc:
219 case llvm::Triple::ppc64:
220 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_powerpc>(
221 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
222 break;
223 case llvm::Triple::ppc64le:
224 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_ppc64le>(
225 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
226 break;
227 case llvm::Triple::systemz:
228 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_s390x>(
229 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
230 break;
231 case llvm::Triple::x86:
232 case llvm::Triple::x86_64:
233 if (is_linux) {
234 m_thread_reg_ctx_sp = std::make_shared<RegisterContextLinuxCore_x86_64>(
235 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
236 } else {
237 m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
238 args&: *this, args&: reg_interface, args&: m_gpregset_data, args&: m_notes);
239 }
240 break;
241 default:
242 break;
243 }
244
245 reg_ctx_sp = m_thread_reg_ctx_sp;
246 } else {
247 reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame);
248 }
249 return reg_ctx_sp;
250}
251
252llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
253ThreadElfCore::GetSiginfo(size_t max_size) const {
254 if (m_siginfo_bytes.empty())
255 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
256 S: "no siginfo note");
257
258 return llvm::MemoryBuffer::getMemBufferCopy(InputData: m_siginfo_bytes,
259 BufferName: "siginfo note bytes");
260}
261
262bool ThreadElfCore::CalculateStopInfo() {
263 ProcessSP process_sp(GetProcess());
264 if (!process_sp)
265 return false;
266
267 PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
268 if (platform_sp) {
269 lldb::StopInfoSP stopinfo_sp = platform_sp->GetStopInfoFromSiginfo(thread&: *this);
270 // The platform SP can optionally handle creating the stop info from the
271 // siginfo value however it's not guaraunteed to be implemented on every
272 // platform, so if we fall through this case, we create from just the signo.
273 if (stopinfo_sp) {
274 SetStopInfo(std::move(stopinfo_sp));
275 return true;
276 }
277 }
278
279 SetStopInfo(StopInfo::CreateStopReasonWithSignal(thread&: *this, signo: m_signo));
280 return true;
281}
282
283// Parse PRSTATUS from NOTE entry
284ELFLinuxPrStatus::ELFLinuxPrStatus() {
285 memset(s: this, c: 0, n: sizeof(ELFLinuxPrStatus));
286}
287
288size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) {
289 constexpr size_t mips_linux_pr_status_size_o32 = 96;
290 constexpr size_t mips_linux_pr_status_size_n32 = 72;
291 constexpr size_t num_ptr_size_members = 10;
292 if (arch.IsMIPS()) {
293 std::string abi = arch.GetTargetABI();
294 assert(!abi.empty() && "ABI is not set");
295 if (abi == "n64")
296 return sizeof(ELFLinuxPrStatus);
297 else if (abi == "o32")
298 return mips_linux_pr_status_size_o32;
299 // N32 ABI
300 return mips_linux_pr_status_size_n32;
301 }
302 switch (arch.GetCore()) {
303 case lldb_private::ArchSpec::eCore_x86_32_i386:
304 case lldb_private::ArchSpec::eCore_x86_32_i486:
305 return 72;
306 default:
307 if (arch.GetAddressByteSize() == 8)
308 return sizeof(ELFLinuxPrStatus);
309 else
310 return sizeof(ELFLinuxPrStatus) - num_ptr_size_members * 4;
311 }
312}
313
314Status ELFLinuxPrStatus::Parse(const DataExtractor &data,
315 const ArchSpec &arch) {
316 Status error;
317 if (GetSize(arch) > data.GetByteSize()) {
318 error = Status::FromErrorStringWithFormat(
319 format: "NT_PRSTATUS size should be %zu, but the remaining bytes are: %" PRIu64,
320 GetSize(arch), data.GetByteSize());
321 return error;
322 }
323
324 // Read field by field to correctly account for endianess of both the core
325 // dump and the platform running lldb.
326 offset_t offset = 0;
327 si_signo = data.GetU32(offset_ptr: &offset);
328 si_code = data.GetU32(offset_ptr: &offset);
329 si_errno = data.GetU32(offset_ptr: &offset);
330
331 pr_cursig = data.GetU16(offset_ptr: &offset);
332 offset += 2; // pad
333
334 pr_sigpend = data.GetAddress(offset_ptr: &offset);
335 pr_sighold = data.GetAddress(offset_ptr: &offset);
336
337 pr_pid = data.GetU32(offset_ptr: &offset);
338 pr_ppid = data.GetU32(offset_ptr: &offset);
339 pr_pgrp = data.GetU32(offset_ptr: &offset);
340 pr_sid = data.GetU32(offset_ptr: &offset);
341
342 pr_utime.tv_sec = data.GetAddress(offset_ptr: &offset);
343 pr_utime.tv_usec = data.GetAddress(offset_ptr: &offset);
344
345 pr_stime.tv_sec = data.GetAddress(offset_ptr: &offset);
346 pr_stime.tv_usec = data.GetAddress(offset_ptr: &offset);
347
348 pr_cutime.tv_sec = data.GetAddress(offset_ptr: &offset);
349 pr_cutime.tv_usec = data.GetAddress(offset_ptr: &offset);
350
351 pr_cstime.tv_sec = data.GetAddress(offset_ptr: &offset);
352 pr_cstime.tv_usec = data.GetAddress(offset_ptr: &offset);
353
354 return error;
355}
356
357static struct compat_timeval
358copy_timespecs(const ProcessInstanceInfo::timespec &oth) {
359 using sec_t = decltype(compat_timeval::tv_sec);
360 using usec_t = decltype(compat_timeval::tv_usec);
361 return {.tv_sec: static_cast<sec_t>(oth.tv_sec), .tv_usec: static_cast<usec_t>(oth.tv_usec)};
362}
363
364std::optional<ELFLinuxPrStatus>
365ELFLinuxPrStatus::Populate(const lldb::ThreadSP &thread_sp) {
366 ELFLinuxPrStatus prstatus{};
367 prstatus.pr_pid = thread_sp->GetID();
368 lldb::ProcessSP process_sp = thread_sp->GetProcess();
369 ProcessInstanceInfo info;
370 if (!process_sp->GetProcessInfo(info))
371 return std::nullopt;
372
373 prstatus.pr_ppid = info.GetParentProcessID();
374 prstatus.pr_pgrp = info.GetProcessGroupID();
375 prstatus.pr_sid = info.GetProcessSessionID();
376 prstatus.pr_utime = copy_timespecs(oth: info.GetUserTime());
377 prstatus.pr_stime = copy_timespecs(oth: info.GetSystemTime());
378 prstatus.pr_cutime = copy_timespecs(oth: info.GetCumulativeUserTime());
379 prstatus.pr_cstime = copy_timespecs(oth: info.GetCumulativeSystemTime());
380 return prstatus;
381}
382
383// Parse PRPSINFO from NOTE entry
384ELFLinuxPrPsInfo::ELFLinuxPrPsInfo() {
385 memset(s: this, c: 0, n: sizeof(ELFLinuxPrPsInfo));
386}
387
388size_t ELFLinuxPrPsInfo::GetSize(const lldb_private::ArchSpec &arch) {
389 constexpr size_t mips_linux_pr_psinfo_size_o32_n32 = 128;
390 if (arch.IsMIPS()) {
391 uint8_t address_byte_size = arch.GetAddressByteSize();
392 if (address_byte_size == 8)
393 return sizeof(ELFLinuxPrPsInfo);
394 return mips_linux_pr_psinfo_size_o32_n32;
395 }
396
397 switch (arch.GetCore()) {
398 case lldb_private::ArchSpec::eCore_s390x_generic:
399 case lldb_private::ArchSpec::eCore_x86_64_x86_64:
400 return sizeof(ELFLinuxPrPsInfo);
401 case lldb_private::ArchSpec::eCore_x86_32_i386:
402 case lldb_private::ArchSpec::eCore_x86_32_i486:
403 return 124;
404 default:
405 return 0;
406 }
407}
408
409Status ELFLinuxPrPsInfo::Parse(const DataExtractor &data,
410 const ArchSpec &arch) {
411 Status error;
412 ByteOrder byteorder = data.GetByteOrder();
413 if (GetSize(arch) > data.GetByteSize()) {
414 error = Status::FromErrorStringWithFormat(
415 format: "NT_PRPSINFO size should be %zu, but the remaining bytes are: %" PRIu64,
416 GetSize(arch), data.GetByteSize());
417 return error;
418 }
419 size_t size = 0;
420 offset_t offset = 0;
421
422 pr_state = data.GetU8(offset_ptr: &offset);
423 pr_sname = data.GetU8(offset_ptr: &offset);
424 pr_zomb = data.GetU8(offset_ptr: &offset);
425 pr_nice = data.GetU8(offset_ptr: &offset);
426 if (data.GetAddressByteSize() == 8) {
427 // Word align the next field on 64 bit.
428 offset += 4;
429 }
430
431 pr_flag = data.GetAddress(offset_ptr: &offset);
432
433 if (arch.IsMIPS()) {
434 // The pr_uid and pr_gid is always 32 bit irrespective of platforms
435 pr_uid = data.GetU32(offset_ptr: &offset);
436 pr_gid = data.GetU32(offset_ptr: &offset);
437 } else {
438 // 16 bit on 32 bit platforms, 32 bit on 64 bit platforms
439 pr_uid = data.GetMaxU64(offset_ptr: &offset, byte_size: data.GetAddressByteSize() >> 1);
440 pr_gid = data.GetMaxU64(offset_ptr: &offset, byte_size: data.GetAddressByteSize() >> 1);
441 }
442
443 pr_pid = data.GetU32(offset_ptr: &offset);
444 pr_ppid = data.GetU32(offset_ptr: &offset);
445 pr_pgrp = data.GetU32(offset_ptr: &offset);
446 pr_sid = data.GetU32(offset_ptr: &offset);
447
448 size = 16;
449 data.ExtractBytes(offset, length: size, dst_byte_order: byteorder, dst: pr_fname);
450 offset += size;
451
452 size = 80;
453 data.ExtractBytes(offset, length: size, dst_byte_order: byteorder, dst: pr_psargs);
454 offset += size;
455
456 return error;
457}
458
459std::optional<ELFLinuxPrPsInfo>
460ELFLinuxPrPsInfo::Populate(const lldb::ProcessSP &process_sp) {
461 ProcessInstanceInfo info;
462 if (!process_sp->GetProcessInfo(info))
463 return std::nullopt;
464
465 return Populate(info, state: process_sp->GetState());
466}
467
468std::optional<ELFLinuxPrPsInfo>
469ELFLinuxPrPsInfo::Populate(const lldb_private::ProcessInstanceInfo &info,
470 lldb::StateType process_state) {
471 ELFLinuxPrPsInfo prpsinfo{};
472 prpsinfo.pr_pid = info.GetProcessID();
473 prpsinfo.pr_nice = info.GetPriorityValue().value_or(u: 0);
474 prpsinfo.pr_zomb = 0;
475 if (auto zombie_opt = info.IsZombie(); zombie_opt.value_or(u: false)) {
476 prpsinfo.pr_zomb = 1;
477 }
478 /**
479 * In the linux kernel this comes from:
480 * state = READ_ONCE(p->__state);
481 * i = state ? ffz(~state) + 1 : 0;
482 * psinfo->pr_sname = (i > 5) ? '.' : "RSDTZW"[i];
483 *
484 * So we replicate that here. From proc_pid_stats(5)
485 * R = Running
486 * S = Sleeping on uninterrutible wait
487 * D = Waiting on uninterruptable disk sleep
488 * T = Tracing stop
489 * Z = Zombie
490 * W = Paging
491 */
492 switch (process_state) {
493 case lldb::StateType::eStateSuspended:
494 prpsinfo.pr_sname = 'S';
495 prpsinfo.pr_state = 1;
496 break;
497 case lldb::StateType::eStateStopped:
498 [[fallthrough]];
499 case lldb::StateType::eStateStepping:
500 prpsinfo.pr_sname = 'T';
501 prpsinfo.pr_state = 3;
502 break;
503 case lldb::StateType::eStateUnloaded:
504 [[fallthrough]];
505 case lldb::StateType::eStateRunning:
506 prpsinfo.pr_sname = 'R';
507 prpsinfo.pr_state = 0;
508 break;
509 default:
510 break;
511 }
512
513 /**
514 * pr_flags is left as 0. The values (in linux) are specific
515 * to the kernel. We recover them from the proc filesystem
516 * but don't put them in ProcessInfo because it would really
517 * become very linux specific and the utility here seems pretty
518 * dubious
519 */
520
521 if (info.EffectiveUserIDIsValid())
522 prpsinfo.pr_uid = info.GetUserID();
523
524 if (info.EffectiveGroupIDIsValid())
525 prpsinfo.pr_gid = info.GetGroupID();
526
527 if (info.ParentProcessIDIsValid())
528 prpsinfo.pr_ppid = info.GetParentProcessID();
529
530 if (info.ProcessGroupIDIsValid())
531 prpsinfo.pr_pgrp = info.GetProcessGroupID();
532
533 if (info.ProcessSessionIDIsValid())
534 prpsinfo.pr_sid = info.GetProcessSessionID();
535
536 constexpr size_t fname_len = std::extent_v<decltype(prpsinfo.pr_fname)>;
537 static_assert(fname_len > 0, "This should always be non zero");
538 const llvm::StringRef fname = info.GetNameAsStringRef();
539 auto fname_begin = fname.begin();
540 std::copy_n(first: fname_begin, n: std::min(a: fname_len, b: fname.size()),
541 result: prpsinfo.pr_fname);
542 prpsinfo.pr_fname[fname_len - 1] = '\0';
543 auto args = info.GetArguments();
544 auto argentry_iterator = std::begin(cont&: args);
545 char *psargs = prpsinfo.pr_psargs;
546 char *psargs_end = std::end(arr&: prpsinfo.pr_psargs);
547 while (psargs < psargs_end && argentry_iterator != args.end()) {
548 llvm::StringRef argentry = argentry_iterator->ref();
549 size_t len =
550 std::min<size_t>(a: std::distance(first: psargs, last: psargs_end), b: argentry.size());
551 auto arg_iterator = std::begin(cont&: argentry);
552 psargs = std::copy_n(first: arg_iterator, n: len, result: psargs);
553 if (psargs != psargs_end)
554 *(psargs++) = ' ';
555 ++argentry_iterator;
556 }
557 *(psargs - 1) = '\0';
558 return prpsinfo;
559}
560

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp