cpp/profiler/SamplingProfiler.h (112 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.
*/
#pragma once
#include "TimerManager.h"
#include <semaphore.h>
#include <setjmp.h>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#if !defined(__GLIBCXX__)
#include <__threading_support>
#endif
#include <fbjni/fbjni.h>
#include <profilo/ExternalApiGlue.h>
#include <profilo/MultiBufferLogger.h>
#include <profilo/profiler/BaseTracer.h>
#include <profilo/profiler/Constants.h>
#include <profilo/profiler/SignalHandler.h>
namespace fbjni = facebook::jni;
using facebook::profilo::logger::MultiBufferLogger;
namespace facebook {
namespace profilo {
namespace profiler {
enum StackSlotState {
FREE = StackCollectionRetcode::MAXVAL + 1,
BUSY,
BUSY_WITH_METADATA,
};
//
// Slots are preallocated storage for the sampling profiler. They
// are necessary because unwinding happens in a signal context and thus
// allocation via the traditional APIs is not possible.
//
// The slot state encodes the tid in the high 16 bits and the
// state (StackSlotState) in the lower 16 bits.
//
// Each slot goes through a lifecycle:
// FREE -> BUSY -> BUSY_WITH_METADATA -> {StackCollectionRetcode}
//
struct StackSlot {
std::atomic<uint64_t> state;
uint16_t depth;
int64_t time;
sigjmp_buf sig_jmp_buf;
uint32_t profilerType;
ThreadTimer::Type timerType;
int64_t frames[MAX_STACK_DEPTH]; // frame pointer addresses
char const* method_names[MAX_STACK_DEPTH];
char const* class_descriptors[MAX_STACK_DEPTH];
#ifdef PROFILER_COLLECT_PC
u2 pcs[MAX_STACK_DEPTH];
#endif
StackSlot() : state(StackSlotState::FREE), depth(0) {}
};
struct Whitelist {
std::unordered_set<int32_t> whitelistedThreads;
std::mutex whitelistedThreadsMtx; // Guards whitelistedThreads
};
struct ProfileState {
pid_t processId;
MultiBufferLogger* logger;
int availableTracers;
int currentTracers;
std::unordered_map<int32_t, std::shared_ptr<BaseTracer>> tracersMap;
int64_t profileStartTime;
std::atomic_bool isProfiling{};
// Slots/Stacks
StackSlot stacks[MAX_STACKS_COUNT];
std::atomic<uint32_t> currentSlot;
std::atomic<uint32_t> fullSlotsCounter;
// Error stats
std::atomic<uint16_t> errSigCrashes;
std::atomic<uint16_t> errSlotMisses;
std::atomic<uint16_t> errStackOverflows;
// Logger
sem_t slotsCounterSem;
std::atomic_bool isLoggerLoopDone;
// Config parameters
bool cpuClockModeEnabled;
bool wallClockModeEnabled;
int threadDetectIntervalMs;
int samplingRateMs;
// When in "wall clock mode", we can optionally whitelist additional threads
// to profile as well.
std::shared_ptr<Whitelist> whitelist;
std::unique_ptr<TimerManager> timerManager;
// If a secondary trace starts, we need to tell the logger loop to clear
// its cache of logged frames, so that the new trace won't miss any symbols
std::atomic_bool resetFrameworkSymbols;
};
/**
* Exported functions
*/
class SamplingProfiler {
public:
static SamplingProfiler& getInstance();
SamplingProfiler() : state_() {
state_.whitelist = std::make_shared<Whitelist>();
}
bool initialize(
MultiBufferLogger& logger,
int32_t available_tracers,
std::unordered_map<int32_t, std::shared_ptr<BaseTracer>> tracers);
void loggerLoop();
void stopProfiling();
bool startProfiling(
int requested_providers,
int sampling_rate_ms,
int thread_detect_interval_ms,
bool cpu_clock_mode_enabled,
bool wall_clock_mode_enabled);
void addToWhitelist(int targetThread);
void removeFromWhitelist(int targetThread);
void resetFrameworkNamesSet();
private:
ProfileState state_;
struct {
SignalHandler* sigsegv;
SignalHandler* sigbus;
SignalHandler* sigprof;
} signal_handlers_;
// Profiling timer management
bool startProfilingTimers();
bool stopProfilingTimers();
void registerSignalHandlers();
void unregisterSignalHandlers();
// Logger
void maybeSignalReader();
void flushStackTraces(std::unordered_set<uint64_t>& loggedFramesSet);
static void FaultHandler(SignalHandler::HandlerScope, int, siginfo_t*, void*);
static void
UnwindStackHandler(SignalHandler::HandlerScope, int, siginfo_t*, void*);
friend class SamplingProfilerTestAccessor;
};
} // namespace profiler
} // namespace profilo
} // namespace facebook