Android 平台下的 Method Trace 实现解析

作者:卓_修武
转载地址:https://juejin.cn/post/7107137302043820039

Android 中的MethodTrace

对于开发者来说,Android的Java 层 提供了两种开发者可直接调用的 Method Trace 的API,一是 android.os.Debug类中的 startMethodTracing相关API,第二个 是android.os.Trace 类中的beginSection相关AP。 这两者的区别是 Debug类只能监控 Java函数调用, 而Trace类底层是使用 atrace 实现,其追踪的函数会包含了应用及系统的Java 和Native函数,并且底层基于ftrace还可以追踪cpu的详细活动信息。

本文主要是为了分析Java层的Method Trace,因此主要研究 Debug类的 startMethodTracing的底层实现。 在拓展部分 介绍的 Java堆栈采样生成火焰图的方式 已开源到GitHub上 供参考 github.com/Knight-ZXW/…

系统 Method Trace 实现解析

直接进入主题 ,本节会将Method Trace 分位 启动Trace、Trace进行中、结束Trace三个阶段进行分析。

启动Trace

当调用Debug.startMethodTracing 函数开始Trace时,其在native层的实际调用的API为 art/runtime/trace.cc 的 Trace::Start() 函数,该函数参数包含以下入参: 写入的File指针,buffer大小,flags信息、TraceOutputMode(输出模式)、TraceMode(trace实现方式)。 该函数核心的逻辑在于 需要根据TraceMode的值 采取不同的 函数调用监听方案。 TraceMode的定义只有两种,分为 kMethodTracing 以及kSampling, 分别对应在Java层调用Debug.startMethodTracing 以及 Debug.startMethodTracingSamping 。

void Trace::Start(std::unique_ptr<File>&& trace_file_in,
                  size_t buffer_size,
                  int flags,
                  TraceOutputMode output_mode,
                  TraceMode trace_mode,
                  int interval_us)

  //.. 省略
  //create Trace  
  {
    // Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
    gc::ScopedGCCriticalSection gcs(self,
                                    gc::kGcCauseInstrumentation,
                                    gc::kCollectorTypeInstrumentation);
    ScopedSuspendAll ssa(__FUNCTION__);
    MutexLock mu(self, *Locks::trace_lock_);
    if (the_trace_ != nullptr) {
      //已经存在trace 实例,忽略本次调用 
      LOG(ERROR) << "Trace already in progress, ignoring this request";

    } else {
      enable_stats = (flags & kTraceCountAllocs) != 0;
      the_trace_ = new Trace(trace_file.release(), buffer_size, flags, output_mode, trace_mode);
      if (trace_mode == TraceMode::kSampling) {
        CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread,
                                            reinterpret_cast<void*>(interval_us)),
                                            "Sampling profiler thread");
        the_trace_->interval_us_ = interval_us;
      } else {
        runtime->GetInstrumentation()->AddListener(
            the_trace_,
            instrumentation::Instrumentation::kMethodEntered |
                instrumentation::Instrumentation::kMethodExited |
                instrumentation::Instrumentation::kMethodUnwind);
        // TODO: In full-PIC mode, we don't need to fully deopt.
        // TODO: We can only use trampoline entrypoints if we are java-debuggable since in that case
        // we know that inlining and other problematic optimizations are disabled. We might just
        // want to use the trampolines anyway since it is faster. It makes the story with disabling
        // jit-gc more complex though.
        runtime->GetInstrumentation()->EnableMethodTracing(
            kTracerInstrumentationKey, /*needs_interpreter=*/!runtime->IsJavaDebuggable());
      }
    }
  }}

if (the_trace_ != nullptr) {
      //已经存在trace 实例,忽略本次调用 
      LOG(ERROR) << "Trace already in progress, ignoring this request";

    }

在实现中,可以看到 在创建Trace实例时,如果判断当前已经存在trace实例(the_trace 变量),则会忽略这次调用。如果不存在,才会调用Trace构造函数函数,创建出trace实例,因此在一次Trace流程未结束前,多次调用StartTrace是无效的。

当真正开始创建Trace实例时,是通用 new Trace()创建的,创建实例之后,开始根据不同的 TraceMode,进行真正的函数调用监听实现。 这里根据TraceMode 分为了 采样类型(TraceMode::kSampling) 以及 插桩类型(TraceMode::kMethodTracing)的方式。

Trace 过程

采样类型 Trace

先关注下采样类型的实现,首先会通过 pthread_create 创建一个采样工作线程,这个线程执行的是 Trace::

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值