kill -3dumpStackTraces

本文探讨了一个由线程D状态引发的watchdog问题。通过分析线程的调用栈,发现无法获取到native/kernel级别的调用栈信息,进一步研究发现这是由于特定条件下线程堆栈的过滤机制所致。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在看一个watchdog问题,是由于线程D状态引起的,但是没有打印出native/kernel的调用栈,不知道这个线程导致block在哪里了,不太好分析问题,调用栈如下:

"Binder:1176_17" prio=5 tid=120 Native
  | group="main" sCount=1 dsCount=0 obj=0x140c3100 self=0x7f74a52a00
  | sysTid=2774 nice=0 cgrp=default sched=0/0 handle=0x7f71f00440
  | state=D schedstat=( 71108648427 27941348162 142494 ) utm=4190 stm=2920 core=4 HZ=100
  | stack=0x7f71e04000-0x7f71e06000 stackSize=1013KB
  | held mutexes=
  at com.android.server.power.PowerManagerService.nativeReleaseSuspendBlocker(Native method)
  at com.android.server.power.PowerManagerService.access$3900(PowerManagerService.java:101)
  at com.android.server.power.PowerManagerService$SuspendBlockerImpl.release(PowerManagerService.java:3523)
  - locked <0x01889afd> (a com.android.server.power.PowerManagerService$SuspendBlockerImpl)
  at com.android.server.power.PowerManagerService.updateSuspendBlockerLocked(PowerManagerService.java:2570)
  at com.android.server.power.PowerManagerService.updatePowerStateLocked(PowerManagerService.java:1643)
  at com.android.server.power.PowerManagerService.removeWakeLockLocked(PowerManagerService.java:1106)
  at com.android.server.power.PowerManagerService.releaseWakeLockInternal(PowerManagerService.java:1080)
  - locked <0x09ee3aa7> (a java.lang.Object)
  at com.android.server.power.PowerManagerService.access$4400(PowerManagerService.java:101)
  at com.android.server.power.PowerManagerService$BinderService.releaseWakeLock(PowerManagerService.java:3604)
  at android.os.IPowerManager$Stub.onTransact(IPowerManager.java:94)
  at android.os.Binder.execTransact(Binder.java:460)


所以花时间调查一个dumpStackTraces没有打印出kernel和native调用栈的问题,大概看了一下调用流程:

void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map) const {
  DumpState(os);
  DumpStack(os, backtrace_map);
}

继续看看DumpStack方法:

void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map) const {
  // TODO: we call this code when dying but may not have suspended the thread ourself. The
  //       IsSuspended check is therefore racy with the use for dumping (normally we inhibit
  //       the race with the thread_suspend_count_lock_).
  bool dump_for_abort = (gAborting > 0);
  bool safe_to_dump = (this == Thread::Current() || IsSuspended());
  if (!kIsDebugBuild) {
    // We always want to dump the stack for an abort, however, there is no point dumping another
    // thread's stack in debug builds where we'll hit the not suspended check in the stack walk.
    safe_to_dump = (safe_to_dump || dump_for_abort);
  }
  if (safe_to_dump) {
    // If we're currently in native code, dump that stack before dumping the managed stack.
    if (dump_for_abort || ShouldShowNativeStack(this)) {
      DumpKernelStack(os, GetTid(), "  kernel: ", false);
      ArtMethod* method = GetCurrentMethod(nullptr, !dump_for_abort);
      DumpNativeStack(os, GetTid(), backtrace_map, "  native: ", method);
    }
    DumpJavaStack(os);
  } else {
    os << "Not able to dump stack of thread that isn't suspended";
  }
}
这里会调用DumpKernelStack/DumpNativeStack/DumpJavaStack/方法,但是在调用前有一个判断:ShouldShowNativeStack

static bool ShouldShowNativeStack(const Thread* thread)
    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  ThreadState state = thread->GetState();

  #ifdef MTK_ART_DONT_DUMP_NATIVE_STACK_FOR_LARGE_PROCESS
  if ((thread->GetThreadId()) < 8 || (thread->GetThreadId() > 20)) {
    return false;
  }
  #endif

  // In native code somewhere in the VM (one of the kWaitingFor* states)? That's interesting.
  if (state > kWaiting && state < kStarting) {
    return true;
  }

  // In an Object.wait variant or Thread.sleep? That's not interesting.
  if (state == kTimedWaiting || state == kSleeping || state == kWaiting) {
    return false;
  }

  // Threads with no managed stack frames should be shown.
  const ManagedStack* managed_stack = thread->GetManagedStack();
  if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr &&
      managed_stack->GetTopShadowFrame() == nullptr)) {
    return true;
  }

  // In some other native method? That's interesting.
  // We don't just check kNative because native methods will be in state kSuspended if they're
  // calling back into the VM, or kBlocked if they're blocked on a monitor, or one of the
  // thread-startup states if it's early enough in their life cycle (http://b/7432159).
  ArtMethod* current_method = thread->GetCurrentMethod(nullptr);
  return current_method != nullptr && current_method->IsNative();
}


上边这里进行了一系列的判断,里边有一个mtk加的代码,线程号小于8大于20就返回false.....对比了一下traces.txt文件,确实是只打印了8-20号线程的调用栈。。。。。‘

  #ifdef MTK_ART_DONT_DUMP_NATIVE_STACK_FOR_LARGE_PROCESS
  if ((thread->GetThreadId()) < 8 || (thread->GetThreadId() > 20)) {
    return false;
  }
  #endif



<think>嗯,用户现在问的是关于`kill -3`的示例和效果。之前他们问过`kill`命令的所有选项,所以可能已经对`kill`的基本用法和常见信号有一定了解了。现在需要具体解释`-3`这个信号,也就是SIGQUIT。 首先,我需要回忆一下SIGQUIT的作用。SIGQUIT的编号是3,当进程收到这个信号时,默认行为是终止进程并生成核心转储(core dump)。核心转储文件对调试很有用,因为它保存了进程崩溃时的内存状态、堆栈跟踪等信息。用户可能是在开发或调试时遇到问题,需要了解如何触发核心转储来排查错误。 接下来,要确定如何正确使用`kill -3`。在之前的回复中,已经列出了信号编号和名称,所以这里需要具体说明使用方式,比如通过进程ID发送信号,或者使用信号名称。另外,用户可能需要示例来理解实际应用场景,比如如何生成核心转储,以及如何配置系统以允许生成核心转储文件,因为有时候系统默认会限制core文件的大小为0,导致无法生成。 然后,需要考虑用户可能遇到的问题。例如,用户执行`kill -3`后没有看到core文件,可能是因为系统设置的问题。这时候需要解释如何检查ulimit设置,如何调整core文件大小,以及core文件的存储位置和命名规则。可能还需要提到权限问题,比如进程是否有权限写入core文件的目录,或者磁盘空间是否足够。 另外,用户可能对核心转储文件的使用不太熟悉,可能需要简要说明如何用调试工具(如gdb)分析core文件。虽然用户没有明确问这个,但提供这些信息可以增加回答的实用性。 还要注意使用场景的示例。比如在Java应用中,`kill -3`可能会触发线程转储(thread dump),而不是核心转储,尤其是当JVM处理SIGQUIT信号时。这可能是一个特殊情况,需要特别指出,避免用户混淆。 最后,确保回答结构清晰,分点说明效果、使用示例、注意事项和扩展知识,这样用户能够逐步理解和应用。同时,避免使用过于技术化的术语,保持解释的易懂性,特别是考虑到用户可能刚接触这些概念。</think>### `kill -3` 的示例与效果详解 `kill -3` 用于向进程发送 **`SIGQUIT` 信号**(编号3)。该信号默认会**终止进程并生成核心转储文件(core dump)**,常用于调试程序崩溃问题。 --- #### **1. `SIGQUIT` 信号的作用** - **默认行为**: - 终止进程。 - 生成核心转储文件(`core` 或 `core.<PID>`),记录进程崩溃时的内存状态、堆栈跟踪等信息。 - **触发方式**:终端中按 `Ctrl+\` 也会发送 `SIGQUIT`。 --- #### **2. 使用示例** ##### **场景**:调试一个崩溃的程序 1. **运行测试程序**: ```bash # 编写一个会崩溃的测试程序(如 C 程序) ./crash_program & # 后台运行 ``` ```bash [1] 12345 # 假设进程 PID 为 12345 ``` 2. **发送 `SIGQUIT` 信号**: ```bash kill -3 12345 # 或 kill -s QUIT 12345 ``` - 进程终止,并在当前目录生成 `core` 或 `core.12345` 文件。 3. **分析核心转储文件**: ```bash gdb ./crash_program core.12345 # 使用调试工具查看崩溃原因 ``` --- #### **3. 核心转储文件配置** - **检查核心转储是否启用**: ```bash ulimit -c # 输出为 0 表示禁用,非0表示允许生成(单位:KB) ``` - **临时启用核心转储**: ```bash ulimit -c unlimited # 允许生成任意大小的 core 文件 ``` - **永久配置**(修改 `/etc/security/limits.conf`): ``` * soft core unlimited ``` --- #### **4. 注意事项** - **文件路径与命名**: - 默认生成在当前工作目录,可通过修改 `/proc/sys/kernel/core_pattern` 自定义路径: ```bash echo "/tmp/core-%e-%p" > /proc/sys/kernel/core_pattern # %e=程序名,%p=PID ``` - **权限问题**: - 确保进程对目标目录有写入权限。 - 检查磁盘空间是否充足。 - **Java 等虚拟机程序**: - 对 JVM 进程发送 `kill -3` 会触发 **线程转储(thread dump)**,输出到标准错误或日志文件(而非生成 core 文件)。 --- #### **5. 扩展:`SIGQUIT` vs `SIGSEGV`** | 信号 | 触发原因 | 核心转储 | 典型场景 | |-------------|----------------------------|----------|--------------------------| | `SIGQUIT` | 用户主动发送(如 `Ctrl+\`)| ✔️ | 调试时手动触发崩溃 | | `SIGSEGV` | 内存非法访问(段错误) | ✔️ | 程序访问无效内存地址 | --- **总结**:`kill -3` 是调试程序崩溃的重要工具,通过生成核心转储文件帮助定位问题。使用时需确保系统允许生成 core 文件,并熟悉调试工具(如 `gdb`)的分析方法。对于 Java 应用,需注意其特殊行为(生成线程转储而非 core 文件)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值