/* * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved. * 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. */ #ifndef STACK_PREPROCESS_H #define STACK_PREPROCESS_H #include #include #include #include #include #include #include #include #include "hook_record_factory.h" #include "hook_record.h" #include "logging.h" #include "nocopyable.h" #include "stack_data_repeater.h" #include "buffer_writer.h" #include "virtual_runtime.h" #include "hook_common.h" #include "native_hook_config.pb.h" #include "native_hook_result.pb.h" #include "native_hook_result.pbencoder.h" #include "safe_map.h" #include "schedule_task_manager.h" #include "common.h" #include "stack_builder.h" namespace OHOS::Developtools::NativeDaemon { using WriterStructPtr = std::unique_ptr::pointer; struct RecordStatistic { uint32_t pid {0}; uint32_t callstackId {0}; uint32_t tagId {0}; RecordStatisticsEvent::MemoryType type {RecordStatisticsEvent::MALLOC}; uint64_t applyCount {0}; uint64_t releaseCount {0}; uint64_t applySize {0}; uint64_t releaseSize {0}; }; struct CallStackHash { uint64_t operator()(const std::vector& stack) const { return std::accumulate(stack.begin(), stack.end(), 0); } }; class StackPreprocess : public std::enable_shared_from_this { public: explicit StackPreprocess(const StackDataRepeaterPtr& dataRepeater, const NativeHookConfig& hookConfig, clockid_t pluginDataClockId, FILE* fpHookData = nullptr, bool isHookStandalone = false, bool isSaService = false, bool isProtobufSerialize = true); ~StackPreprocess(); void SetWriter(const std::shared_ptr& writer); void SetWriter(const WriterStructPtr& writer); void SetFactory(std::shared_ptr factory) { factory_ = factory; } bool StartTakeResults(); bool StopTakeResults(); void FinishTraceFile(); bool FlushRecordStatistics(); void FlushRecordApplyAndReleaseMatchData(); void ForceStop(); inline void SetPid(int32_t pid) { pid_ = pid; } inline void InitStatisticsTime() { lastStatisticsTime_ = std::chrono::steady_clock::now(); } void SaveMemTag(uint32_t tagId, const std::string& tagName); bool GetMemTag(uint32_t tagId, std::string& tagName); void SaveJsRawStack(uint64_t jsChainId, const char* jsRawStack); const char* GetJsRawStack(uint64_t jsChainId); void ReportBasicData(); void WriteHookConfig(); void TakeResultsFromShmem(const std::shared_ptr&, const std::shared_ptr&); void SetNmdFd(uint32_t fd) { nmdFd_ = fd; } void SetFlushSize(uint64_t size) { double tenth = static_cast(size) / 10.0; flushSize_ = static_cast(std::ceil(tenth)); PROFILER_LOG_INFO(LOG_CORE, "SetFlushSize size: %" PRIu64 ", flushSize_: %" PRIu64 "", size, flushSize_); if (isProtobufSerialize_) { bufferSize_ = flushSize_ << 1; buffer_ = std::make_unique(bufferSize_); } } private: using CallFrame = OHOS::Developtools::NativeDaemon::CallFrame; struct ElfSymbolTable { uint64_t textVaddr; uint32_t textOffset; uint32_t symEntSize; std::vector strTable; std::vector symTable; }; enum RecordStatisticsLimit : std::size_t { STATISTICS_MAP_SZIE = 100000, STATISTICS_PERIOD_DATA_SIZE = 100000, ALLOC_ADDRMAMP_SIZE = 100000, MATCH_ADDRMAMP_SIZE = 100000, }; struct ScopedLockFile { ScopedLockFile(FILE* fpHook): fpHookData(fpHook) { flockfile(fpHookData); } ~ScopedLockFile() { funlockfile(fpHookData); } FILE* fpHookData {nullptr}; }; private: void TakeResults(); template void SetHookData(HookRecordPtr hookRecord, T& stackData); template void SetHookData(HookRecordPtr hookRecord, std::vector& callFrames, T& stackData); void WriteFrames(HookRecordPtr hookRecord, const std::vector& callFrames); template void SetFrameInfo(T& frame, CallFrame& callFrame); template void ReportSymbolNameMap(CallFrame& callFrame, T& stackData); template void ReportFilePathMap(CallFrame& callFrame, T& stackData); template void ReportFrameMap(CallFrame& callFrame, T& stackData); void ReportThreadNameMap(uint32_t tid, const std::string& tname); void SetMapsInfo(); template void SetSymbolInfo(uint32_t filePathId, ElfSymbolTable& symbolInfo, T& batchNativeHookData); template void FlushCheck(T& stackData); void FlushData(BatchNativeHookData& stackData); void FlushData(OHOS::Developtools::Profiler::ProtoEncoder::BatchNativeHookData& stackData); void Flush(const uint8_t* src, size_t size); void GetSymbols(const std::string& filePath, ElfSymbolTable& symbols); template void FillOfflineCallStack(std::vector& callFrames, size_t idx, T& stackData); template void FillCallStack(std::vector& callFrames, size_t idx, T& stackData); template uint32_t SetCallStackMap(T& stackData); template uint32_t GetCallStackId(const HookRecordPtr& hookRecord, std::vector& callFrames, T& stackData); uint32_t FindCallStackId(std::vector& callStack); template void SetEventFrame(const RawStackPtr& rawStack, std::vector& callFrames, T* event, uint32_t stackId, const std::string& type = ""); template void SetEventFrame(const ReportEventBaseData& rawStack, T* event, uint32_t stackMapId, const std::string& type = ""); template void SetAllocStatisticsFrame(const HookRecordPtr& hookRecord, std::vector& callFrames, T& stackData); template void SetAllocStatisticsFrame(const HookRecordPtr& hookRecord, T& stackData); template void SetApplyAndReleaseMatchFrame(HookRecordPtr hookRecord, std::vector& callFrames, T& stackData); void IntervalFlushRecordStatistics(); void IntervalFlushApplyAndReleaseMatchData(); bool HandleNoStackEvent(HookRecordPtr& hookRecord); bool SetFreeStatisticsData(uint64_t addr); void SetAllocStatisticsData(const HookRecordPtr& hookRecord, size_t stackId, bool isExists = false); unsigned LgFloor(unsigned long x); uint64_t PowCeil(uint64_t x); void ReportOfflineSymbolizationData(); RandomWriteCtx* StartReport(); int32_t FinishReport(); void FillNapiStack(std::string& tagName, std::vector& callFrames, uint64_t napiIndex); private: std::chrono::steady_clock::time_point lastStatisticsTime_ = std::chrono::steady_clock::now(); std::shared_ptr writer_ = nullptr; StackDataRepeaterPtr dataRepeater_ = nullptr; std::thread thread_ {}; std::unique_ptr buffer_; std::atomic_bool isStopTakeData_ = false; std::shared_ptr runtime_instance; DISALLOW_COPY_AND_MOVE(StackPreprocess); OHOS::SafeMap memTagMap_ = {}; std::unordered_map threadNameMap_ = {}; NativeHookConfig hookConfig_; uint32_t ignoreCnts_ = 0; uint32_t eventCnts_ = 0; bool flushBasicData_ {true}; std::vector u64regs_; // Key is callStack_, value is call stack id std::unordered_map, uint32_t, CallStackHash> callStackMap_; std::chrono::seconds statisticsInterval_ {0}; // Key is call stack id, value is recordstatistic data std::unordered_map recordStatisticsMap_; // Key is call stack id, value is recordstatistic data pointer std::unordered_map statisticsPeriodData_; // Key is alloc or mmap address, value is ReportEventBaseData list iterator std::unordered_map::iterator> applyAndReleaseMatchIntervallMap_; std::list applyAndReleaseMatchPeriodListData_; std::list releasedPeriodListData_; std::chrono::seconds applyAndReleaseMatchInterval_{0}; // used for plugin data clockid_t pluginDataClockId_ = CLOCK_REALTIME; // used for clac wait time in StackDataRepeater::TakeRawData() or statistics HookData clockid_t hookDataClockId_ = CLOCK_REALTIME; FILE* fpHookData_ {nullptr}; bool isHookStandaloneSerialize_ {false}; int32_t pid_ {-1}; std::mutex mtx_; std::mutex memTagMtx_; std::mutex jsMapMtx_; COMMON::SpinLock spinLock_; bool isSaService_{false}; std::mutex allocAddrMapMtx_; bool isProtobufSerialize_{true}; WriterStructPtr resultWriter_{nullptr}; std::variant stackData_; uint64_t flushSize_{0}; uint64_t bufferSize_{0}; bool statisticsModelFlushCallstack_{false}; OHOS::Developtools::Profiler::ProtoEncoder::ProfilerPluginData profilerPluginData_; // Key is js stack id , value is js raw stack pointer std::map jsStackMap_ = {}; std::set jsStackSet_ = {}; bool unwindFailReport_ = true; std::vector prctlPeriodTags_; // applyAndReleaseMatchInterval mode used std::atomic napiIndex_{1}; std::atomic endMsgCount_ {0}; ScheduleTaskManager scheduleTaskManager_; uint32_t nmdFd_ = 0; uint32_t dataFlushSize_ = 0; std::shared_ptr factory_{nullptr}; }; } #endif // STACK_PREPROCESS_H