cpp/profiler/ArtUnwindcTracer.cpp (125 lines of code) (raw):

/** * Copyright 2004-present, Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <fb/log.h> #include <unistd.h> #include <atomic> #include "profilo/logger/buffer/RingBuffer.h" #include "profilo/profiler/ArtUnwindcTracer.h" #include "profilo/profiler/BaseTracer.h" #include "profilo/profiler/unwindc/runtime.h" namespace facebook { namespace profilo { namespace profiler { #if ANDROID_VERSION_NUM == 500 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc500; #elif ANDROID_VERSION_NUM == 510 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc510; #elif ANDROID_VERSION_NUM == 600 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc600; #elif ANDROID_VERSION_NUM == 700 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc700; #elif ANDROID_VERSION_NUM == 710 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc710; #elif ANDROID_VERSION_NUM == 711 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc711; #elif ANDROID_VERSION_NUM == 712 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc712; #elif ANDROID_VERSION_NUM == 800 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc800; #elif ANDROID_VERSION_NUM == 810 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc810; #elif ANDROID_VERSION_NUM == 900 static constexpr ArtUnwindcVersion kVersion = kArtUnwindc900; #endif /** * We need to namespace the codegen'd symbols in unwinder.h to appease the * linker. * * Since this compilation unit will only ever have one of these versions * defined at a time, we can just ignore the namespacing by means of * `using namespace`. */ namespace ANDROID_NAMESPACE { // ANDROID_NAMESPACE is a preprocessor variable #include "profilo/profiler/unwindc/unwinder.h" } // namespace ANDROID_NAMESPACE using namespace ANDROID_NAMESPACE; namespace { struct unwinder_data { ucontext_t* ucontext; int64_t* frames; char const** method_names; char const** class_descriptors; uint16_t depth; uint16_t max_depth; }; bool unwind_cb(uintptr_t frame, void* data) { unwinder_data* ud = reinterpret_cast<unwinder_data*>(data); if (ud->depth >= ud->max_depth) { // stack overflow, stop the traversal return false; } ud->frames[ud->depth] = get_method_trace_id(frame); if (ud->method_names != nullptr && ud->class_descriptors != nullptr) { auto declaring_class = get_declaring_class(frame); auto class_string_t = get_class_descriptor(declaring_class); auto method_string_t = get_method_name(frame); ud->method_names[ud->depth] = method_string_t.data; ud->class_descriptors[ud->depth] = class_string_t.data; } ++ud->depth; return true; } } // namespace template <> ArtUnwindcTracer<kVersion>::ArtUnwindcTracer() {} template <> StackCollectionRetcode ArtUnwindcTracer<kVersion>::collectJavaStack( ucontext_t* ucontext, int64_t* frames, char const** method_names, char const** class_descriptors, uint16_t& depth, uint16_t max_depth) { unwinder_data data{ .ucontext = ucontext, .frames = frames, .method_names = method_names, .class_descriptors = class_descriptors, .depth = 0, .max_depth = max_depth, }; depth = 0; if (!unwind(&unwind_cb, &data)) { return StackCollectionRetcode::STACK_OVERFLOW; } depth = data.depth; if (depth == 0) { return StackCollectionRetcode::EMPTY_STACK; } return StackCollectionRetcode::SUCCESS; } template <> StackCollectionRetcode ArtUnwindcTracer<kVersion>::collectStack( ucontext_t* ucontext, int64_t* frames, uint16_t& depth, uint16_t max_depth) { return collectJavaStack(ucontext, frames, nullptr, nullptr, depth, max_depth); } template <> void ArtUnwindcTracer<kVersion>::flushStack( MultiBufferLogger& logger, int64_t* frames, uint16_t depth, int tid, int64_t time_) { logger.write(FramesEntry{ .id = 0, .type = EntryType::STACK_FRAME, .timestamp = static_cast<int64_t>(time_), .tid = tid, .matchid = 0, .frames = {.values = const_cast<int64_t*>(frames), .size = depth}}); } template <> void ArtUnwindcTracer<kVersion>::prepare() { // Preinitialize static state. (void)get_art_thread(); } template <> void ArtUnwindcTracer<kVersion>::startTracing() { prepare(); } template <> void ArtUnwindcTracer<kVersion>::stopTracing() {} } // namespace profiler } // namespace profilo } // namespace facebook