From 6bd33eed9f4bf406da71247991f32af2b5a06411 Mon Sep 17 00:00:00 2001 From: linma Date: Mon, 11 Apr 2022 14:00:08 -0700 Subject: [PATCH] add module phases gcovparser and gcovprofile --- .../maple_driver/src/driver_option_common.cpp | 2 +- src/mapleall/maple_ipa/BUILD.gn | 1 + .../maple_ipa/include/ipa_gcovprofile.h | 44 + .../maple_ipa/src/ipa_gcovprofile.cpp | 87 ++ src/mapleall/maple_ir/include/mir_function.h | 9 + src/mapleall/maple_ir/include/mir_module.h | 9 + src/mapleall/maple_me/include/me_cfg.h | 2 + .../maple_me/include/me_pgo_instrument.h | 9 + .../maple_me/include/me_profile_use.h | 1 + src/mapleall/maple_me/src/me_cfg.cpp | 48 + src/mapleall/maple_me/src/me_profile_use.cpp | 67 +- src/mapleall/maple_phase/include/phases.def | 8 +- .../maple_util/include/gcov_profile.h | 64 ++ src/mapleall/mpl2mpl/BUILD.gn | 2 + src/mapleall/mpl2mpl/include/gcov_parser.h | 35 + src/mapleall/mpl2mpl/src/gcov_parser.cpp | 206 ++++ .../mpl2mpl/src/module_phase_manager.cpp | 3 + .../gcc-linaro-7.5.0-gcov/gcov-counter.def | 54 + third_party/gcc-linaro-7.5.0-gcov/gcov-io.c | 987 ++++++++++++++++++ third_party/gcc-linaro-7.5.0-gcov/gcov-io.h | 425 ++++++++ 20 files changed, 2057 insertions(+), 6 deletions(-) create mode 100644 src/mapleall/maple_ipa/include/ipa_gcovprofile.h create mode 100644 src/mapleall/maple_ipa/src/ipa_gcovprofile.cpp create mode 100644 src/mapleall/maple_util/include/gcov_profile.h create mode 100644 src/mapleall/mpl2mpl/include/gcov_parser.h create mode 100644 src/mapleall/mpl2mpl/src/gcov_parser.cpp create mode 100644 third_party/gcc-linaro-7.5.0-gcov/gcov-counter.def create mode 100644 third_party/gcc-linaro-7.5.0-gcov/gcov-io.c create mode 100644 third_party/gcc-linaro-7.5.0-gcov/gcov-io.h diff --git a/src/mapleall/maple_driver/src/driver_option_common.cpp b/src/mapleall/maple_driver/src/driver_option_common.cpp index e84c980877..b178b6c428 100644 --- a/src/mapleall/maple_driver/src/driver_option_common.cpp +++ b/src/mapleall/maple_driver/src/driver_option_common.cpp @@ -238,7 +238,7 @@ const mapleOption::Descriptor kUsages[] = { kArgCheckPolicyBool, " --profileUse \tOptimize static languages with profile data\n", "driver", - { "me", "mpl2mpl", "mplcg" } }, + { "mpl2mpl" } }, { kGCOnly, kEnable, "", diff --git a/src/mapleall/maple_ipa/BUILD.gn b/src/mapleall/maple_ipa/BUILD.gn index c4b8682172..909eaf1bfc 100755 --- a/src/mapleall/maple_ipa/BUILD.gn +++ b/src/mapleall/maple_ipa/BUILD.gn @@ -36,6 +36,7 @@ src_libmplipa = [ "src/prop_parameter_type.cpp", "src/ipa_collect.cpp", "src/ipa_clone.cpp", + "src/ipa_gcovprofile.cpp", ] configs = [ "${MAPLEALL_ROOT}:mapleallcompilecfg" ] diff --git a/src/mapleall/maple_ipa/include/ipa_gcovprofile.h b/src/mapleall/maple_ipa/include/ipa_gcovprofile.h new file mode 100644 index 0000000000..9ad1658f6b --- /dev/null +++ b/src/mapleall/maple_ipa/include/ipa_gcovprofile.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) [2021] Huawei Technologies Co.,Ltd.All rights reserved. + * + * OpenArkCompiler is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR + * FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +#ifndef MAPLE_IPA_INCLUDE_IPA_GCOVPROFILE_H +#define MAPLE_IPA_INCLUDE_IPA_GCOVPROFILE_H +#include +#include +#include "mempool.h" +#include "mempool_allocator.h" +#include "mir_module.h" +#include "mir_function.h" +#include "me_phase_manager.h" +#include "gcov_parser.h" + +namespace maple { + +class IpaProfile : public MapleModulePhase, public MaplePhaseManager { + public: + explicit IpaProfile(MemPool *mp) : MapleModulePhase(&id, mp), MaplePhaseManager(*mp) {} + ~IpaProfile() override = default; + std::string PhaseName() const override; + PHASECONSTRUCTOR(IpaProfile); + bool PhaseRun(MIRModule &m) override; + AnalysisDataManager *GetResult() { + return result; + } + private: + void GetAnalysisDependence(maple::AnalysisDep &aDep) const override; + AnalysisDataManager *result = nullptr; +}; + +} // namespace maple +#endif // MAPLE_IPA_INCLUDE_IPA_GCOVPROFILE_H diff --git a/src/mapleall/maple_ipa/src/ipa_gcovprofile.cpp b/src/mapleall/maple_ipa/src/ipa_gcovprofile.cpp new file mode 100644 index 0000000000..a0bd27d9b5 --- /dev/null +++ b/src/mapleall/maple_ipa/src/ipa_gcovprofile.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) [2021-2022] Huawei Technologies Co.,Ltd.All rights reserved. + * + * OpenArkCompiler is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR + * FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ +#include "ipa_phase_manager.h" +#include "pme_emit.h" +#include "ipa_gcovprofile.h" + +namespace maple { + +void IpaProfile::GetAnalysisDependence(maple::AnalysisDep &aDep) const { + if (Options::profileUse) { + aDep.AddRequired(); + aDep.AddPreserved(); + } +} + +bool IpaProfile::PhaseRun(MIRModule &m) { + SetQuiet(true); + // keep original options value + bool oldProp = MeOption::propDuringBuild; + bool oldMerge = MeOption::mergeStmts; + uint8 oldOptLevel = MeOption::optLevel; + bool oldLayout = MeOption::layoutWithPredict; + MeOption::mergeStmts = false; + MeOption::propDuringBuild = false; + MeOption::layoutWithPredict = false; + MeOption::optLevel = 3; + + AddPhase("mecfgbuild", true); + AddPhase("profileUse", true); + AddPhase("emitforipa", true); + // Not like other phasemanager which use temp mempool to hold analysis results generated from the sub phases. + // Here we use GetManagerMemPool which lives longer than this phase(manager) itself to hold all the analysis result. + // So the following phase can access the result in this phase. + result = GetManagerMemPool()->New(*GetPhaseMemPool()); + auto iter = m.GetFunctionList().begin(); + for (; iter != m.GetFunctionList().end(); iter++) { + MIRFunction *func = *iter; + if (func == nullptr || func->GetFuncSymbol()->GetStorageClass() == kScUnused || + func->IsEmpty()) { + continue; + } + m.SetCurFunction(func); + auto meFuncMP = std::make_unique(memPoolCtrler, "ipa gcovprofile mempool"); + auto meFuncStackMP = std::make_unique(memPoolCtrler, ""); + MemPool *versMP = new ThreadLocalMemPool(memPoolCtrler, "first verst mempool"); + MeFunction &meFunc = *(meFuncMP->New(&m, func, meFuncMP.get(), *meFuncStackMP, versMP, "unknown")); + func->SetMeFunc(&meFunc); + meFunc.PartialInit(); + if (!IsQuiet()) { + LogInfo::MapleLogger() << "---Preparing Function for scc phase < " << func->GetName() << " > ---\n"; + } + meFunc.Prepare(); + + for (size_t i = 0; i < phasesSequence.size(); ++i) { + const MaplePhaseInfo *phase = MaplePhaseRegister::GetMaplePhaseRegister()->GetPhaseByID(phasesSequence[i]); + if (!IsQuiet()) { + LogInfo::MapleLogger() << " >> Prepare " << (phase->IsAnalysis() ? "analysis" : "transform") + << " Phase [ " << phase->PhaseName() << " ] <<\n"; + } + if (phase->IsAnalysis()) { + (void)RunAnalysisPhase(*phase, *result, meFunc, 1); + } else { + (void)RunTransformPhase(*phase, *result, meFunc, 1); + } + } + } + + // restore option value + MeOption::mergeStmts = oldMerge; + MeOption::propDuringBuild = oldProp; + MeOption::optLevel = oldOptLevel; + MeOption::layoutWithPredict = oldLayout; + return false; +} +} // namespace maple diff --git a/src/mapleall/maple_ir/include/mir_function.h b/src/mapleall/maple_ir/include/mir_function.h index 1bf0b9c700..ff965b80a1 100644 --- a/src/mapleall/maple_ir/include/mir_function.h +++ b/src/mapleall/maple_ir/include/mir_function.h @@ -1179,6 +1179,14 @@ class MIRFunction { void SetIsVisited() { isVisited = true; } + + void SetFuncProfData(GcovFuncInfo *data) { + funcProfData = data; + } + GcovFuncInfo* GetFuncProfData() { + return funcProfData; + } + private: MIRModule *module; // the module that owns this function PUIdx puIdx = 0; // the PU index of this function @@ -1278,6 +1286,7 @@ class MIRFunction { uint32 nCtrs = 0; // number of counters uint64 fileLinenoChksum = 0; uint64 cfgChksum = 0; + GcovFuncInfo *funcProfData = nullptr; void DumpFlavorLoweredThanMmpl() const; MIRFuncType *ReconstructFormals(const std::vector &symbols, bool clearOldArgs); }; diff --git a/src/mapleall/maple_ir/include/mir_module.h b/src/mapleall/maple_ir/include/mir_module.h index 4421bfc2b0..aef9f3ea80 100644 --- a/src/mapleall/maple_ir/include/mir_module.h +++ b/src/mapleall/maple_ir/include/mir_module.h @@ -21,6 +21,7 @@ #include "mpl_logging.h" #include "muid.h" #include "profile.h" +#include "gcov_profile.h" #if MIR_FEATURE_FULL #include #include @@ -253,6 +254,13 @@ class MIRModule { return profile; } + GcovProfileData* GetGcovProfile() { + return gcovProfile; + } + void SetGcovProfile(GcovProfileData* info) { + gcovProfile = info; + } + void SetSomeSymbolNeedForDecl(bool s) { someSymbolNeedForwDecl = s; } @@ -682,6 +690,7 @@ class MIRModule { MapleSet symbolSet; MapleVector symbolDefOrder; Profile profile; + GcovProfileData* gcovProfile; bool someSymbolNeedForwDecl = false; // some symbols' addressses used in initialization std::ostream &out; diff --git a/src/mapleall/maple_me/include/me_cfg.h b/src/mapleall/maple_me/include/me_cfg.h index 85a68db398..1f16871834 100644 --- a/src/mapleall/maple_me/include/me_cfg.h +++ b/src/mapleall/maple_me/include/me_cfg.h @@ -293,6 +293,8 @@ class MeCFG : public AnalysisResult { void BuildSCC(); void UpdateBranchTarget(BB &currBB, const BB &oldTarget, BB &newTarget, MeFunction &func); void SwapBBId(BB &bb1, BB &bb2); + void ConstructBBFreqFromStmtFreq(); + void ConstructStmtFreq(); private: void AddCatchHandlerForTryBB(BB &bb, MapleVector &exitBlocks); diff --git a/src/mapleall/maple_me/include/me_pgo_instrument.h b/src/mapleall/maple_me/include/me_pgo_instrument.h index 16ca3d7c43..958e2fadbb 100644 --- a/src/mapleall/maple_me/include/me_pgo_instrument.h +++ b/src/mapleall/maple_me/include/me_pgo_instrument.h @@ -134,6 +134,14 @@ class PGOInstrument { return (allEdgeSize << edgeSizeInfoShift) | hashCode; } + uint64 ComputeLinenoHash() { + const std::string& fileName = func->GetMIRModule().GetFileName(); + uint64 fileNameHash = DJBHash(fileName.c_str()); + std::string lineNo = std::to_string(func->GetMirFunc()->GetSrcPosition().LineNum()); + uint64 linenoHash = fileNameHash << 32 | DJBHash(lineNo.c_str()); + return linenoHash; + } + void ClearBBGroupInfo() { auto eIt = func->GetCfg()->valid_end(); for (auto bIt = func->GetCfg()->valid_begin(); bIt != eIt; ++bIt) { @@ -141,6 +149,7 @@ class PGOInstrument { bb->ClearGroup(); } } + protected: bool dump; private: diff --git a/src/mapleall/maple_me/include/me_profile_use.h b/src/mapleall/maple_me/include/me_profile_use.h index 9319de4e38..7cde099131 100644 --- a/src/mapleall/maple_me/include/me_profile_use.h +++ b/src/mapleall/maple_me/include/me_profile_use.h @@ -136,6 +136,7 @@ class MeProfUse : public PGOInstrument { bool IsSuccUseProf() const { return succCalcuAllEdgeFreq; } + bool GcovRun(); private: bool IsAllZero(Profile::BBInfo &result) const; void SetEdgeCount(BBUseEdge &edge, size_t value); diff --git a/src/mapleall/maple_me/src/me_cfg.cpp b/src/mapleall/maple_me/src/me_cfg.cpp index a217e09df4..0f6b7fef3f 100644 --- a/src/mapleall/maple_me/src/me_cfg.cpp +++ b/src/mapleall/maple_me/src/me_cfg.cpp @@ -1806,6 +1806,50 @@ void MeCFG::SwapBBId(BB &bb1, BB &bb2) { bb2.SetBBId(tmp); } +// set bb frequency from stmt record +void MeCFG::ConstructBBFreqFromStmtFreq() { + GcovProfileData* gcovData = func.GetMIRModule().GetGcovProfile(); + if (!gcovData) return; + GcovFuncInfo* funcData = gcovData->GetFuncProfile(func.GetUniqueID()); + if (!funcData) return; + auto eIt = valid_end(); + for (auto bIt = valid_begin(); bIt != eIt; ++bIt) { + if ((*bIt)->IsEmpty()) continue; + StmtNode first = (*bIt)->GetFirst(); + if (funcData->stmtFreqs.count(first.GetStmtID()) > 0) { + (*bIt)->SetFrequency(funcData->stmtFreqs[first.GetStmtID()]); + } else { + StmtNode last = (*bIt)->GetLast(); + if (funcData->stmtFreqs.count(last.GetStmtID()) > 0) { + (*bIt)->SetFrequency(funcData->stmtFreqs[last.GetStmtID()]); + } + } + } +} + +void MeCFG::ConstructStmtFreq() { + GcovFuncInfo* funcData = func.GetMirFunc()->GetFuncProfData(); + if (!funcData) return; + auto eIt = valid_end(); + // clear stmtFreqs + funcData->stmtFreqs.clear(); + for (auto bIt = valid_begin(); bIt != eIt; ++bIt) { + auto *bb = *bIt; + if (bIt == common_entry()) { + funcData->entry_freq = bb->GetFrequency(); + } + for (auto &stmt : bb->GetStmtNodes()) { + Opcode op = stmt.GetOpCode(); + // record bb start/end stmt + if (stmt.GetStmtID() == bb->GetFirst().GetStmtID() || + stmt.GetStmtID() == bb->GetLast().GetStmtID() || + IsCallAssigned(op) || op == OP_call) { + funcData->stmtFreqs[stmt.GetStmtID()] = bb->GetFrequency(); + } + } + } +} + bool MEMeCfg::PhaseRun(MeFunction &f) { if (!f.IsPme() && f.GetPreMeFunc() != nullptr) { GetAnalysisInfoHook()->ForceEraseAllAnalysisPhase(); @@ -1837,6 +1881,10 @@ bool MEMeCfg::PhaseRun(MeFunction &f) { if (!f.GetMIRModule().IsJavaModule() && MeOption::unifyRets) { theCFG->UnifyRetBBs(); } + // construct bb freq from stmt freq + if (Options::profileUse) { + theCFG->ConstructBBFreqFromStmtFreq(); + } theCFG->Verify(); return false; } diff --git a/src/mapleall/maple_me/src/me_profile_use.cpp b/src/mapleall/maple_me/src/me_profile_use.cpp index ceae4f7d18..100467fb55 100644 --- a/src/mapleall/maple_me/src/me_profile_use.cpp +++ b/src/mapleall/maple_me/src/me_profile_use.cpp @@ -294,6 +294,62 @@ bool MeProfUse::Run() { return true; } +bool MeProfUse::GcovRun() { + GcovProfileData* gcovData = func->GetMIRModule().GetGcovProfile(); + if (!gcovData) return false; + GcovFuncInfo* funcData = gcovData->GetFuncProfile(func->GetUniqueID()); + func->GetMirFunc()->SetFuncProfData(funcData); + if (!funcData) return false; + // early return if lineno fail + uint64 linenohash = ComputeLinenoHash(); + uint32 lineNoChkSum = static_cast((linenohash >> 32) ^ (linenohash & 0xffffffff)); + if (lineNoChkSum != funcData->lineno_checksum) { + if (dump) { + LogInfo::MapleLogger() << func->GetName() << " lineno checksum doesn't match gcda value " + << funcData->lineno_checksum << " lineno real hash " << lineNoChkSum << '\n'; + } + return false; + } + FindInstrumentEdges(); + // early return if cfgchecksum fail + uint64 cfghash = ComputeFuncHash(); + uint32 cfgChkSum = static_cast((cfghash >> 32) ^ (cfghash & 0xffffffff)); + if (cfgChkSum != funcData->cfg_checksum) { + if (dump) { + LogInfo::MapleLogger() << func->GetName() << " hash doesn't match profile hash " + << funcData->cfg_checksum << " func real hash " << cfgChkSum << '\n'; + } + return false; + } + std::vector instrumentBBs; + GetInstrumentBBs(instrumentBBs); + if (dump) { + DumpEdgeInfo(); + } + if (instrumentBBs.size() != funcData->num_counts) { + if (dump) { + LogInfo::MapleLogger() << func->GetName() << " counter doesn't match profile counter " + << funcData->num_counts << " func real counter " << instrumentBBs.size() << '\n'; + } + return false; + } + size_t i = 0; + for (auto *bb : instrumentBBs) { + auto *bbUseInfo = GetOrCreateBBUseInfo(*bb); + bbUseInfo->SetCount(funcData->counts[i]); + i++; + } + InitBBEdgeInfo(); + ComputeEdgeFreq(); + succCalcuAllEdgeFreq = true; + // save edge frequence to bb + SetFuncEdgeInfo(); + func->GetCfg()->ConstructStmtFreq(); + return true; +} + + + void MEProfUse::GetAnalysisDependence(maple::AnalysisDep &aDep) const { aDep.AddRequired(); aDep.SetPreservedAll(); @@ -301,8 +357,15 @@ void MEProfUse::GetAnalysisDependence(maple::AnalysisDep &aDep) const { bool MEProfUse::PhaseRun(maple::MeFunction &f) { MeProfUse profUse(f, *GetPhaseMemPool(), DEBUGFUNC_NEWPM(f)); - (void)profUse.Run(); - if (DEBUGFUNC_NEWPM(f) && profUse.IsSuccUseProf()) { + bool result = true; + if (Options::profileUse) { + result = profUse.GcovRun(); + } else { + profUse.Run(); + } + + //if (DEBUGFUNC_NEWPM(f) && profUse.IsSuccUseProf()) { + if (result) { LogInfo::MapleLogger() << "******************after profile use dump function******************\n"; profUse.DumpFuncCFGEdgeFreq(); f.GetCfg()->DumpToFile("afterProfileUse", false, true); diff --git a/src/mapleall/maple_phase/include/phases.def b/src/mapleall/maple_phase/include/phases.def index a945fad1d7..bb7fded5f5 100644 --- a/src/mapleall/maple_phase/include/phases.def +++ b/src/mapleall/maple_phase/include/phases.def @@ -7,14 +7,16 @@ ADDMODULEPHASE("reflectionanalysis", JAVALANG) ADDMODULEPHASE("GenNativeStubFunc", JAVALANG) ADDMODULEPHASE("gencheckcast", JAVALANG) ADDMODULEPHASE("javaintrnlowering", JAVALANG) -ADDMODULEPHASE("callgraph", true) -ADDMODULEPHASE("inline", Options::O2 && Options::useInline) +ADDMODULEPHASE("callgraph", !Options::profileUse) +ADDMODULEPHASE("inline", Options::O2 && Options::useInline && !Options::profileUse) // run callgraph again to delete fully inlined static function -ADDMODULEPHASE("callgraph", Options::O2 && Options::useInline) +ADDMODULEPHASE("callgraph", Options::O2 && Options::useInline && !Options::profileUse) ADDMODULEPHASE("simplify", Options::O2) ADDMODULEPHASE("ConstantFold", Options::O2) // ipa phase manager +ADDMODULEPHASE("ipaprofile", CLANG && Options::profileUse) ADDMODULEPHASE("IpaSccPM", CLANG && (Options::O2 || Options::profileGen)) +ADDMODULEPHASE("inline", CLANG && (Options::O2 && Options::profileUse)) ADDMODULEPHASE("ipaclone", CLANG && Options::O2 && Options::enableIPAClone) ADDMODULEPHASE("ProfileGenPM", CLANG && Options::profileGen) // me phase manager is also a phase itself. diff --git a/src/mapleall/maple_util/include/gcov_profile.h b/src/mapleall/maple_util/include/gcov_profile.h new file mode 100644 index 0000000000..11375b92d5 --- /dev/null +++ b/src/mapleall/maple_util/include/gcov_profile.h @@ -0,0 +1,64 @@ +#ifndef MAPLE_UTIL_INCLUDE_GCOV_PROFILE_H +#define MAPLE_UTIL_INCLUDE_GCOV_PROFILE_H + +#include "mempool_allocator.h" +#include +#include + +namespace maple { +typedef unsigned gcov_unsigned_t; +typedef int64_t gcov_type; +typedef uint64_t gcov_type_unsigned; +typedef unsigned location_t; + +class GcovFuncInfo { +public: + GcovFuncInfo(MapleAllocator* alloc, unsigned funcIdent, unsigned lineno_cs, unsigned cfg_cs) : + ident(funcIdent), lineno_checksum(lineno_cs), cfg_checksum(cfg_cs), counts(alloc->Adapter()) {}; + int64_t GetFuncFrequency() const { return entry_freq; } + void SetStmtFreq(uint32_t stmtID, int64_t freq) { + stmtFreqs[stmtID] = freq; + } + int64_t GetStmtFreq(uint32_t stmtID) { + if (stmtFreqs.count(stmtID) > 0) { + return stmtFreqs[stmtID]; + } + return 0; + } + + /* Name of function. */ + char *name; + unsigned ident; + unsigned lineno_checksum; + unsigned cfg_checksum; + + /* Array of basic blocks. Like in GCC, the entry block is + at blocks[0] and the exit block is at blocks[1]. */ +#if 0 + block_t *blocks; + unsigned num_blocks; + unsigned blocks_executed; +#endif + /* Raw arc coverage counts. */ + unsigned num_counts; + MapleVector counts; + int64_t entry_freq; // record entry bb frequence + std::unordered_map stmtFreqs; // stmt_id is key, counter value +}; + +class GcovProfileData { +public: + GcovProfileData(MapleAllocator *alloc) : funcsCounter(alloc->Adapter()) {} + + GcovFuncInfo *GetFuncProfile(unsigned puidx) { + if (funcsCounter.count(puidx) > 0) { + return funcsCounter[puidx]; + } + return nullptr; + } + + MapleUnorderedMap funcsCounter; // use puidx as key +}; + +} // end namespace +#endif // MAPLE_UTIL_INCLUDE_GCOV_PROFILE_H diff --git a/src/mapleall/mpl2mpl/BUILD.gn b/src/mapleall/mpl2mpl/BUILD.gn index b4dfefbedd..4497d8e2a7 100755 --- a/src/mapleall/mpl2mpl/BUILD.gn +++ b/src/mapleall/mpl2mpl/BUILD.gn @@ -23,6 +23,7 @@ include_directories = [ "${MAPLEALL_ROOT}/mpl2mpl/include", "${MAPLEALL_ROOT}/mempool/include", "${THIRD_PARTY_ROOT}/bounds_checking_function/include", + "${THIRD_PARTY_ROOT}/gcc-linaro-7.5.0-gcov/", ] src_libmpl2mpl = [ @@ -54,6 +55,7 @@ src_libmpl2mpl = [ "src/call_graph.cpp", "src/inline.cpp", "src/method_replace.cpp", + "src/gcov_parser.cpp", ] configs = [ "${MAPLEALL_ROOT}:mapleallcompilecfg" ] diff --git a/src/mapleall/mpl2mpl/include/gcov_parser.h b/src/mapleall/mpl2mpl/include/gcov_parser.h new file mode 100644 index 0000000000..f1044bfa19 --- /dev/null +++ b/src/mapleall/mpl2mpl/include/gcov_parser.h @@ -0,0 +1,35 @@ +#ifndef MAPLE_MPL2MPL_INCLUDE_GCOVPROFUSE_H +#define MAPLE_MPL2MPL_INCLUDE_GCOVPROFUSE_H + +#include "mempool.h" +#include "mempool_allocator.h" +//#include "me_pgo_instrument.h" +#include "bb.h" +#include "maple_phase_manager.h" +#include "gcov_profile.h" + +namespace maple { + +class MGcovParser : public AnalysisResult { + public: + MGcovParser(MIRModule &mirmod, MemPool *memPool, bool debug) : AnalysisResult(memPool), m(mirmod), + alloc(memPool), localMP(memPool), gcovData(nullptr), dumpDetail(debug) {} + virtual ~MGcovParser() = default; + int read_count_file(); + void DumpFuncInfo(); + + GcovProfileData *GetGcovData() { return gcovData; } + + private: + MIRModule &m; + MapleAllocator alloc; + MemPool *localMP; + GcovProfileData *gcovData; + bool dumpDetail; +}; + +MAPLE_MODULE_PHASE_DECLARE(M2MGcovParser) + +} // end of namespace maple + +#endif // MAPLE_MPL2MPL_INCLUDE_GCOVPROFUSE_H diff --git a/src/mapleall/mpl2mpl/src/gcov_parser.cpp b/src/mapleall/mpl2mpl/src/gcov_parser.cpp new file mode 100644 index 0000000000..26bed02f28 --- /dev/null +++ b/src/mapleall/mpl2mpl/src/gcov_parser.cpp @@ -0,0 +1,206 @@ +/* Dump a gcov file, for debugging use. + Copyright (C) 2002-2021 Free Software Foundation, Inc. + Contributed by Nathan Sidwell + +Gcov is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +Gcov is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Gcov; see the file COPYING3. If not see +. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "option.h" +#include "mpl_logging.h" +#include "gcov_parser.h" + +// macro define used in gcov-io +typedef unsigned char boolean; +#define HOST_WIDE_INT long long +#define CHAR_BIT __CHAR_BIT__ + +#ifndef false +#define false 0 +#endif + +#ifndef true +#define true 1 +#endif + +typedef unsigned gcov_unsigned_t; +typedef int64_t gcov_type; +typedef uint64_t gcov_type_unsigned; +typedef unsigned location_t; +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif + +#define XNEWVEC(T, N) ((T *) malloc (sizeof (T) * (N))) +#define XRESIZEVAR(T, P, S) ((T *) realloc ((P), (S))) + +/* Return the number of set bits in X. */ +static int popcount_hwi (unsigned HOST_WIDE_INT x) { + int i, ret = 0; + size_t bits = sizeof (x) * CHAR_BIT; + + for (i = 0; i < bits; i += 1) + { + ret += x & 1; + x >>= 1; + } + + return ret; +} + +#define input_location 0 +#define gcc_assert(EXPR) ((void)(0 && (EXPR))) +#define IS_DIR_SEPARATOR_1(dos_based, c) \ + (((c) == '/') \ + || (((c) == '\\') && (dos_based))) +#define IS_UNIX_DIR_SEPARATOR(c) IS_DIR_SEPARATOR_1 (0, c) +#define IS_DIR_SEPARATOR(c) IS_UNIX_DIR_SEPARATOR (c) +#define xmalloc malloc + +static void fatal_error (location_t loc, const char *gmsgid, ...) { + va_list ap; + va_start (ap, gmsgid); + va_end (ap); + assert(0); +} + + +#define IN_GCOV (-1) +#include "gcov-io.h" +#include "gcov-io.c" + +namespace maple { +static unsigned object_runs; +static unsigned program_count; +//static unsigned bbg_stamp = 0; +// reference +int MGcovParser::read_count_file() { + std::string gcovDataFile = Options::profile; + if (gcovDataFile.empty()) { + const std::string& fileName = m.GetFileName(); + std::string::size_type lastDot = fileName.find_last_of('.'); + ASSERT(lastDot != std::string::npos, "sanity check"); + // search default directory + if (const char* env_p = std::getenv("GCOV_PREFIX")) { + gcovDataFile.append(env_p); + } else { + gcovDataFile.append("."); + } + gcovDataFile.append("/"); + gcovDataFile.append(fileName.substr(0, lastDot)); + gcovDataFile.append(".gcda"); + } + if (!gcov_open(gcovDataFile.c_str(), 1)) { + LogInfo::MapleLogger() << "no data file " << gcovDataFile << " \n"; + return 0; + } + + if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) { + LogInfo::MapleLogger() << gcovDataFile << " not a gcov data file\n"; + gcov_close (); + return 1; + } + unsigned version = gcov_read_unsigned (); + if (version != GCOV_VERSION) { + char v[4], e[4]; + GCOV_UNSIGNED2STRING (v, version); + GCOV_UNSIGNED2STRING (e, GCOV_VERSION); + LogInfo::MapleLogger() << gcovDataFile << " version " << v << " prefer version " << e << "\n"; + } + unsigned tag = gcov_read_unsigned (); +#if 0 + // stamp value is generated by time function, the value should be same in .gcno file + // now skip compare + if (tag != bbg_stamp) { + LogInfo::MapleLogger() << gcovDataFile << " stamp mismatch \n"; + gcov_close (); + return 1; + } +#endif + gcovData = localMP->New(&alloc); + GcovFuncInfo *funcInfo = nullptr; + while ((tag = gcov_read_unsigned ())) { + unsigned length = gcov_read_unsigned (); + unsigned long base = gcov_position (); + + if (tag == GCOV_TAG_PROGRAM_SUMMARY) { + struct gcov_summary summary; + gcov_read_summary (&summary); + object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs; + program_count++; + } else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH) { + unsigned ident; + /* Try to find the function in the list. To speed up the + search, first start from the last function found. */ + ident = gcov_read_unsigned (); + unsigned lineno_checksum = gcov_read_unsigned (); + unsigned cfg_checksum = gcov_read_unsigned (); + funcInfo = localMP->New(&alloc, ident, lineno_checksum, cfg_checksum); + (gcovData->funcsCounter)[ident] = funcInfo; + } else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS)) { + funcInfo->num_counts = GCOV_TAG_COUNTER_NUM(length); + for (int ix = 0; ix != funcInfo->num_counts; ix++) { + funcInfo->counts.push_back(gcov_read_counter()); + } + } + gcov_sync (base, length); + } + gcov_close(); + + if (dumpDetail) { + DumpFuncInfo(); + } + return 0; +} + +void MGcovParser::DumpFuncInfo() { + for (auto &it : gcovData->funcsCounter) { + GcovFuncInfo *funcInfo = it.second; + LogInfo::MapleLogger() << "\nfunction ident " << std::dec << funcInfo->ident; + LogInfo::MapleLogger() << " lino_checksum 0x" << std::hex << funcInfo->lineno_checksum; + LogInfo::MapleLogger() << " cfg_checksum 0x" << std::hex << funcInfo->cfg_checksum << "\n"; + LogInfo::MapleLogger() << " num_counts " << std::dec << funcInfo->num_counts << " : "; + for (int i = 0; i < funcInfo->num_counts; i++) { + LogInfo::MapleLogger() << std::dec << " " << funcInfo->counts[i]; + } + } + LogInfo::MapleLogger() << "\n" ; +} + +void M2MGcovParser::GetAnalysisDependence(AnalysisDep &aDep) const { + aDep.SetPreservedAll(); +} + +bool M2MGcovParser::PhaseRun(maple::MIRModule &m) { + MemPool *memPool = GetPhaseMemPool(); + MGcovParser gcovParser(m, memPool, true); + + int res = gcovParser.read_count_file(); + if (res) { + // something wrong + return false; + } + m.SetGcovProfile(gcovParser.GetGcovData()); + return true; +} + + +} // end namespace maple diff --git a/src/mapleall/mpl2mpl/src/module_phase_manager.cpp b/src/mapleall/mpl2mpl/src/module_phase_manager.cpp index 2c5c0b3d61..27e1fcaf75 100644 --- a/src/mapleall/mpl2mpl/src/module_phase_manager.cpp +++ b/src/mapleall/mpl2mpl/src/module_phase_manager.cpp @@ -16,6 +16,7 @@ #include "me_phase_manager.h" #include "ipa_phase_manager.h" #include "ipa_side_effect.h" +#include "ipa_gcovprofile.h" #define JAVALANG (mirModule.IsJavaModule()) #define CLANG (mirModule.IsCModule()) @@ -107,6 +108,8 @@ MAPLE_ANALYSIS_PHASE_REGISTER(M2MCallGraph, callgraph) MAPLE_ANALYSIS_PHASE_REGISTER(M2MKlassHierarchy, classhierarchy) MAPLE_ANALYSIS_PHASE_REGISTER(M2MAnnotationAnalysis, annotationanalysis) MAPLE_ANALYSIS_PHASE_REGISTER(ProfileGenPM, ProfileGenPM) +MAPLE_ANALYSIS_PHASE_REGISTER(M2MGcovParser, gcovparser) +MAPLE_ANALYSIS_PHASE_REGISTER(IpaProfile, ipaprofile) MAPLE_TRANSFORM_PHASE_REGISTER(M2MInline, inline) MAPLE_TRANSFORM_PHASE_REGISTER(M2MIPODevirtualize, ipodevirtulize) diff --git a/third_party/gcc-linaro-7.5.0-gcov/gcov-counter.def b/third_party/gcc-linaro-7.5.0-gcov/gcov-counter.def new file mode 100644 index 0000000000..85dcdae593 --- /dev/null +++ b/third_party/gcc-linaro-7.5.0-gcov/gcov-counter.def @@ -0,0 +1,54 @@ +/* Definitions for the gcov counters in the GNU compiler. + Copyright (C) 2001-2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* Before including this file, define a macro: + + DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) + + This macro will be expanded to all supported gcov counters, their + names, or the type of handler functions. FN_TYPE will be + expanded to a handler function, like in gcov_merge, it is + expanded to __gcov_merge ## FN_TYPE. */ + +/* Arc transitions. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS, "arcs", _add) + +/* Histogram of value inside an interval. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INTERVAL, "interval", _add) + +/* Histogram of exact power2 logarithm of a value. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_POW2, "pow2", _add) + +/* The most common value of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_SINGLE, "single", _single) + +/* The most common indirect address. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INDIR, "indirect_call", _single) + +/* Compute average value passed to the counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_AVERAGE, "average", _add) + +/* IOR of the all values passed to counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior) + +/* Time profile collecting first run of a function */ +DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", _time_profile) + +/* Top N value tracking for indirect calls. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_ICALL_TOPNV, "indirect_call_topn", _icall_topn) diff --git a/third_party/gcc-linaro-7.5.0-gcov/gcov-io.c b/third_party/gcc-linaro-7.5.0-gcov/gcov-io.c new file mode 100644 index 0000000000..64dedd5528 --- /dev/null +++ b/third_party/gcc-linaro-7.5.0-gcov/gcov-io.c @@ -0,0 +1,987 @@ +/* File format for coverage information + Copyright (C) 1996-2017 Free Software Foundation, Inc. + Contributed by Bob Manson . + Completely remangled by Nathan Sidwell . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* Routines declared in gcov-io.h. This file should be #included by + another source file, after having #included gcov-io.h. */ + +#if !IN_GCOV +static void gcov_write_block (unsigned); +static gcov_unsigned_t *gcov_write_words (unsigned); +#endif +static const gcov_unsigned_t *gcov_read_words (unsigned); +#if !IN_LIBGCOV +static void gcov_allocate (unsigned); +#endif + +/* Optimum number of gcov_unsigned_t's read from or written to disk. */ +#define GCOV_BLOCK_SIZE (1 << 10) + +struct gcov_var +{ + FILE *file; + gcov_position_t start; /* Position of first byte of block */ + unsigned offset; /* Read/write position within the block. */ + unsigned length; /* Read limit in the block. */ + unsigned overread; /* Number of words overread. */ + int error; /* < 0 overflow, > 0 disk error. */ + int mode; /* < 0 writing, > 0 reading */ +#if IN_LIBGCOV + /* Holds one block plus 4 bytes, thus all coverage reads & writes + fit within this buffer and we always can transfer GCOV_BLOCK_SIZE + to and from the disk. libgcov never backtracks and only writes 4 + or 8 byte objects. */ + gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1]; +#else + int endian; /* Swap endianness. */ + /* Holds a variable length block, as the compiler can write + strings and needs to backtrack. */ + size_t alloc; + gcov_unsigned_t *buffer; +#endif +} gcov_var; + +/* Save the current position in the gcov file. */ +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +gcov_position_t +gcov_position (void) +{ + gcov_nonruntime_assert (gcov_var.mode > 0); + return gcov_var.start + gcov_var.offset; +} + +/* Return nonzero if the error flag is set. */ +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +int +gcov_is_error (void) +{ + return gcov_var.file ? gcov_var.error : 1; +} + +#if IN_LIBGCOV +/* Move to beginning of file and initialize for writing. */ +GCOV_LINKAGE inline void +gcov_rewrite (void) +{ + gcov_var.mode = -1; + gcov_var.start = 0; + gcov_var.offset = 0; + fseek (gcov_var.file, 0L, SEEK_SET); +} +#endif + +static inline gcov_unsigned_t from_file (gcov_unsigned_t value) +{ +#if !IN_LIBGCOV + if (gcov_var.endian) + { + value = (value >> 16) | (value << 16); + value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff); + } +#endif + return value; +} + +/* Open a gcov file. NAME is the name of the file to open and MODE + indicates whether a new file should be created, or an existing file + opened. If MODE is >= 0 an existing file will be opened, if + possible, and if MODE is <= 0, a new file will be created. Use + MODE=0 to attempt to reopen an existing file and then fall back on + creating a new one. If MODE > 0, the file will be opened in + read-only mode. Otherwise it will be opened for modification. + Return zero on failure, non-zero on success. */ + +GCOV_LINKAGE int +#if IN_LIBGCOV +gcov_open (const char *name) +#else +gcov_open (const char *name, int mode) +#endif +{ +#if IN_LIBGCOV + int mode = 0; +#endif +#if GCOV_LOCKED + struct flock s_flock; + int fd; + + s_flock.l_whence = SEEK_SET; + s_flock.l_start = 0; + s_flock.l_len = 0; /* Until EOF. */ + s_flock.l_pid = getpid (); +#endif + + gcov_nonruntime_assert (!gcov_var.file); + gcov_var.start = 0; + gcov_var.offset = gcov_var.length = 0; + gcov_var.overread = -1u; + gcov_var.error = 0; +#if !IN_LIBGCOV + gcov_var.endian = 0; +#endif +#if GCOV_LOCKED + if (mode > 0) + { + /* Read-only mode - acquire a read-lock. */ + s_flock.l_type = F_RDLCK; + /* pass mode (ignored) for compatibility */ + fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR); + } + else + { + /* Write mode - acquire a write-lock. */ + s_flock.l_type = F_WRLCK; + /* Truncate if force new mode. */ + fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666); + } + if (fd < 0) + return 0; + + while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR) + continue; + + gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b"); + + if (!gcov_var.file) + { + close (fd); + return 0; + } +#else + if (mode >= 0) + /* Open an existing file. */ + gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b"); + + if (gcov_var.file) + mode = 1; + else if (mode <= 0) + /* Create a new file. */ + gcov_var.file = fopen (name, "w+b"); + + if (!gcov_var.file) + return 0; +#endif + + gcov_var.mode = mode ? mode : 1; + + setbuf (gcov_var.file, (char *)0); + + return 1; +} + +/* Close the current gcov file. Flushes data to disk. Returns nonzero + on failure or error flag set. */ + +GCOV_LINKAGE int +gcov_close (void) +{ + if (gcov_var.file) + { +#if !IN_GCOV + if (gcov_var.offset && gcov_var.mode < 0) + gcov_write_block (gcov_var.offset); +#endif + fclose (gcov_var.file); + gcov_var.file = 0; + gcov_var.length = 0; + } +#if !IN_LIBGCOV + free (gcov_var.buffer); + gcov_var.alloc = 0; + gcov_var.buffer = 0; +#endif + gcov_var.mode = 0; + return gcov_var.error; +} + +#if !IN_LIBGCOV +/* Check if MAGIC is EXPECTED. Use it to determine endianness of the + file. Returns +1 for same endian, -1 for other endian and zero for + not EXPECTED. */ + +GCOV_LINKAGE int +gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected) +{ + if (magic == expected) + return 1; + magic = (magic >> 16) | (magic << 16); + magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff); + if (magic == expected) + { + gcov_var.endian = 1; + return -1; + } + return 0; +} +#endif + +#if !IN_LIBGCOV +static void +gcov_allocate (unsigned length) +{ + size_t new_size = gcov_var.alloc; + + if (!new_size) + new_size = GCOV_BLOCK_SIZE; + new_size += length; + new_size *= 2; + + gcov_var.alloc = new_size; + gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2); +} +#endif + +#if !IN_GCOV +/* Write out the current block, if needs be. */ + +static void +gcov_write_block (unsigned size) +{ + if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) + gcov_var.error = 1; + gcov_var.start += size; + gcov_var.offset -= size; +} + +/* Allocate space to write BYTES bytes to the gcov file. Return a + pointer to those bytes, or NULL on failure. */ + +static gcov_unsigned_t * +gcov_write_words (unsigned words) +{ + gcov_unsigned_t *result; + + gcov_nonruntime_assert (gcov_var.mode < 0); +#if IN_LIBGCOV + if (gcov_var.offset >= GCOV_BLOCK_SIZE) + { + gcov_write_block (GCOV_BLOCK_SIZE); + if (gcov_var.offset) + { + memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); + } + } +#else + if (gcov_var.offset + words > gcov_var.alloc) + gcov_allocate (gcov_var.offset + words); +#endif + result = &gcov_var.buffer[gcov_var.offset]; + gcov_var.offset += words; + + return result; +} + +/* Write unsigned VALUE to coverage file. Sets error flag + appropriately. */ + +GCOV_LINKAGE void +gcov_write_unsigned (gcov_unsigned_t value) +{ + gcov_unsigned_t *buffer = gcov_write_words (1); + + buffer[0] = value; +} + +/* Write counter VALUE to coverage file. Sets error flag + appropriately. */ + +#if IN_LIBGCOV +GCOV_LINKAGE void +gcov_write_counter (gcov_type value) +{ + gcov_unsigned_t *buffer = gcov_write_words (2); + + buffer[0] = (gcov_unsigned_t) value; + if (sizeof (value) > sizeof (gcov_unsigned_t)) + buffer[1] = (gcov_unsigned_t) (value >> 32); + else + buffer[1] = 0; +} +#endif /* IN_LIBGCOV */ + +#if !IN_LIBGCOV +/* Write STRING to coverage file. Sets error flag on file + error, overflow flag on overflow */ + +GCOV_LINKAGE void +gcov_write_string (const char *string) +{ + unsigned length = 0; + unsigned alloc = 0; + gcov_unsigned_t *buffer; + + if (string) + { + length = strlen (string); + alloc = (length + 4) >> 2; + } + + buffer = gcov_write_words (1 + alloc); + + buffer[0] = alloc; + + if (alloc > 0) + { + buffer[alloc] = 0; /* place nul terminators. */ + memcpy (&buffer[1], string, length); + } +} +#endif + +#if !IN_LIBGCOV +/* Write a tag TAG and reserve space for the record length. Return a + value to be used for gcov_write_length. */ + +GCOV_LINKAGE gcov_position_t +gcov_write_tag (gcov_unsigned_t tag) +{ + gcov_position_t result = gcov_var.start + gcov_var.offset; + gcov_unsigned_t *buffer = gcov_write_words (2); + + buffer[0] = tag; + buffer[1] = 0; + + return result; +} + +/* Write a record length using POSITION, which was returned by + gcov_write_tag. The current file position is the end of the + record, and is restored before returning. Returns nonzero on + overflow. */ + +GCOV_LINKAGE void +gcov_write_length (gcov_position_t position) +{ + unsigned offset; + gcov_unsigned_t length; + gcov_unsigned_t *buffer; + + gcov_nonruntime_assert (gcov_var.mode < 0); + gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset); + gcov_nonruntime_assert (position >= gcov_var.start); + offset = position - gcov_var.start; + length = gcov_var.offset - offset - 2; + buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset]; + buffer[1] = length; + if (gcov_var.offset >= GCOV_BLOCK_SIZE) + gcov_write_block (gcov_var.offset); +} + +#else /* IN_LIBGCOV */ + +/* Write a tag TAG and length LENGTH. */ + +GCOV_LINKAGE void +gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) +{ + gcov_unsigned_t *buffer = gcov_write_words (2); + + buffer[0] = tag; + buffer[1] = length; +} + +/* Write a summary structure to the gcov file. Return nonzero on + overflow. */ + +GCOV_LINKAGE void +gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) +{ + unsigned ix, h_ix, bv_ix, h_cnt = 0; + const struct gcov_ctr_summary *csum; + unsigned histo_bitvector[GCOV_HISTOGRAM_BITVECTOR_SIZE]; + + /* Count number of non-zero histogram entries, and fill in a bit vector + of non-zero indices. The histogram is only currently computed for arc + counters. */ + for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) + histo_bitvector[bv_ix] = 0; + csum = &summary->ctrs[GCOV_COUNTER_ARCS]; + for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++) + if (csum->histogram[h_ix].num_counters) + { + histo_bitvector[h_ix / 32] |= 1 << (h_ix % 32); + h_cnt++; + } + gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH (h_cnt)); + gcov_write_unsigned (summary->checksum); + for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) + { + gcov_write_unsigned (csum->num); + gcov_write_unsigned (csum->runs); + gcov_write_counter (csum->sum_all); + gcov_write_counter (csum->run_max); + gcov_write_counter (csum->sum_max); + if (ix != GCOV_COUNTER_ARCS) + { + for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) + gcov_write_unsigned (0); + continue; + } + for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) + gcov_write_unsigned (histo_bitvector[bv_ix]); + for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++) + { + if (!csum->histogram[h_ix].num_counters) + continue; + gcov_write_unsigned (csum->histogram[h_ix].num_counters); + gcov_write_counter (csum->histogram[h_ix].min_value); + gcov_write_counter (csum->histogram[h_ix].cum_value); + } + } +} +#endif /* IN_LIBGCOV */ + +#endif /*!IN_GCOV */ + +/* Return a pointer to read BYTES bytes from the gcov file. Returns + NULL on failure (read past EOF). */ + +static const gcov_unsigned_t * +gcov_read_words (unsigned words) +{ + const gcov_unsigned_t *result; + unsigned excess = gcov_var.length - gcov_var.offset; + + if (gcov_var.mode <= 0) + return NULL; + + if (excess < words) + { + gcov_var.start += gcov_var.offset; + if (excess) + { +#if IN_LIBGCOV + memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); +#else + memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, + excess * 4); +#endif + } + gcov_var.offset = 0; + gcov_var.length = excess; +#if IN_LIBGCOV + excess = GCOV_BLOCK_SIZE; +#else + if (gcov_var.length + words > gcov_var.alloc) + gcov_allocate (gcov_var.length + words); + excess = gcov_var.alloc - gcov_var.length; +#endif + excess = fread (gcov_var.buffer + gcov_var.length, + 1, excess << 2, gcov_var.file) >> 2; + gcov_var.length += excess; + if (gcov_var.length < words) + { + gcov_var.overread += words - gcov_var.length; + gcov_var.length = 0; + return 0; + } + } + result = &gcov_var.buffer[gcov_var.offset]; + gcov_var.offset += words; + return result; +} + +/* Read unsigned value from a coverage file. Sets error flag on file + error, overflow flag on overflow */ + +GCOV_LINKAGE gcov_unsigned_t +gcov_read_unsigned (void) +{ + gcov_unsigned_t value; + const gcov_unsigned_t *buffer = gcov_read_words (1); + + if (!buffer) + return 0; + value = from_file (buffer[0]); + return value; +} + +/* Read counter value from a coverage file. Sets error flag on file + error, overflow flag on overflow */ + +GCOV_LINKAGE gcov_type +gcov_read_counter (void) +{ + gcov_type value; + const gcov_unsigned_t *buffer = gcov_read_words (2); + + if (!buffer) + return 0; + value = from_file (buffer[0]); + if (sizeof (value) > sizeof (gcov_unsigned_t)) + value |= ((gcov_type) from_file (buffer[1])) << 32; + else if (buffer[1]) + gcov_var.error = -1; + + return value; +} + +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) +/* Read string from coverage file. Returns a pointer to a static + buffer, or NULL on empty string. You must copy the string before + calling another gcov function. */ + +GCOV_LINKAGE const char * +gcov_read_string (void) +{ + unsigned length = gcov_read_unsigned (); + + if (!length) + return 0; + + return (const char *) gcov_read_words (length); +} +#endif + +GCOV_LINKAGE void +gcov_read_summary (struct gcov_summary *summary) +{ + unsigned ix, h_ix, bv_ix, h_cnt = 0; + struct gcov_ctr_summary *csum; + unsigned histo_bitvector[GCOV_HISTOGRAM_BITVECTOR_SIZE]; + unsigned cur_bitvector; + + summary->checksum = gcov_read_unsigned (); + for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) + { + csum->num = gcov_read_unsigned (); + csum->runs = gcov_read_unsigned (); + csum->sum_all = gcov_read_counter (); + csum->run_max = gcov_read_counter (); + csum->sum_max = gcov_read_counter (); + memset (csum->histogram, 0, + sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE); + for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) + { + histo_bitvector[bv_ix] = gcov_read_unsigned (); +#if IN_LIBGCOV + /* When building libgcov we don't include system.h, which includes + hwint.h (where popcount_hwi is declared). However, libgcov.a + is built by the bootstrapped compiler and therefore the builtins + are always available. */ + h_cnt += __builtin_popcount (histo_bitvector[bv_ix]); +#else + h_cnt += popcount_hwi (histo_bitvector[bv_ix]); +#endif + } + bv_ix = 0; + h_ix = 0; + cur_bitvector = 0; + while (h_cnt--) + { + /* Find the index corresponding to the next entry we will read in. + First find the next non-zero bitvector and re-initialize + the histogram index accordingly, then right shift and increment + the index until we find a set bit. */ + while (!cur_bitvector) + { + h_ix = bv_ix * 32; + if (bv_ix >= GCOV_HISTOGRAM_BITVECTOR_SIZE) + gcov_error ("corrupted profile info: summary histogram " + "bitvector is corrupt"); + cur_bitvector = histo_bitvector[bv_ix++]; + } + while (!(cur_bitvector & 0x1)) + { + h_ix++; + cur_bitvector >>= 1; + } + if (h_ix >= GCOV_HISTOGRAM_SIZE) + gcov_error ("corrupted profile info: summary histogram " + "index is corrupt"); + + csum->histogram[h_ix].num_counters = gcov_read_unsigned (); + csum->histogram[h_ix].min_value = gcov_read_counter (); + csum->histogram[h_ix].cum_value = gcov_read_counter (); + /* Shift off the index we are done with and increment to the + corresponding next histogram entry. */ + cur_bitvector >>= 1; + h_ix++; + } + } +} + +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) +/* Reset to a known position. BASE should have been obtained from + gcov_position, LENGTH should be a record length. */ + +GCOV_LINKAGE void +gcov_sync (gcov_position_t base, gcov_unsigned_t length) +{ + gcov_nonruntime_assert (gcov_var.mode > 0); + base += length; + if (base - gcov_var.start <= gcov_var.length) + gcov_var.offset = base - gcov_var.start; + else + { + gcov_var.offset = gcov_var.length = 0; + fseek (gcov_var.file, base << 2, SEEK_SET); + gcov_var.start = ftell (gcov_var.file) >> 2; + } +} +#endif + +#if IN_LIBGCOV +/* Move to a given position in a gcov file. */ + +GCOV_LINKAGE void +gcov_seek (gcov_position_t base) +{ + if (gcov_var.offset) + gcov_write_block (gcov_var.offset); + fseek (gcov_var.file, base << 2, SEEK_SET); + gcov_var.start = ftell (gcov_var.file) >> 2; +} +#endif + +#if IN_GCOV > 0 +/* Return the modification time of the current gcov file. */ + +GCOV_LINKAGE time_t +gcov_time (void) +{ + struct stat status; + + if (fstat (fileno (gcov_var.file), &status)) + return 0; + else + return status.st_mtime; +} +#endif /* IN_GCOV */ + +#if !IN_GCOV +/* Determine the index into histogram for VALUE. */ + +#if IN_LIBGCOV +static unsigned +#else +GCOV_LINKAGE unsigned +#endif +gcov_histo_index (gcov_type value) +{ + gcov_type_unsigned v = (gcov_type_unsigned)value; + unsigned r = 0; + unsigned prev2bits = 0; + + /* Find index into log2 scale histogram, where each of the log2 + sized buckets is divided into 4 linear sub-buckets for better + focus in the higher buckets. */ + + /* Find the place of the most-significant bit set. */ + if (v > 0) + { +#if IN_LIBGCOV + /* When building libgcov we don't include system.h, which includes + hwint.h (where floor_log2 is declared). However, libgcov.a + is built by the bootstrapped compiler and therefore the builtins + are always available. */ + r = sizeof (long long) * __CHAR_BIT__ - 1 - __builtin_clzll (v); +#else + /* We use floor_log2 from hwint.c, which takes a HOST_WIDE_INT + that is 64 bits and gcov_type_unsigned is 64 bits. */ + r = floor_log2 (v); +#endif + } + + /* If at most the 2 least significant bits are set (value is + 0 - 3) then that value is our index into the lowest set of + four buckets. */ + if (r < 2) + return (unsigned)value; + + gcov_nonruntime_assert (r < 64); + + /* Find the two next most significant bits to determine which + of the four linear sub-buckets to select. */ + prev2bits = (v >> (r - 2)) & 0x3; + /* Finally, compose the final bucket index from the log2 index and + the next 2 bits. The minimum r value at this point is 2 since we + returned above if r was 2 or more, so the minimum bucket at this + point is 4. */ + return (r - 1) * 4 + prev2bits; +} + +/* Merge SRC_HISTO into TGT_HISTO. The counters are assumed to be in + the same relative order in both histograms, and are matched up + and merged in reverse order. Each counter is assigned an equal portion of + its entry's original cumulative counter value when computing the + new merged cum_value. */ + +static void gcov_histogram_merge (gcov_bucket_type *tgt_histo, + gcov_bucket_type *src_histo) +{ + int src_i, tgt_i, tmp_i = 0; + unsigned src_num, tgt_num, merge_num; + gcov_type src_cum, tgt_cum, merge_src_cum, merge_tgt_cum, merge_cum; + gcov_type merge_min; + gcov_bucket_type tmp_histo[GCOV_HISTOGRAM_SIZE]; + int src_done = 0; + + memset (tmp_histo, 0, sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE); + + /* Assume that the counters are in the same relative order in both + histograms. Walk the histograms from largest to smallest entry, + matching up and combining counters in order. */ + src_num = 0; + src_cum = 0; + src_i = GCOV_HISTOGRAM_SIZE - 1; + for (tgt_i = GCOV_HISTOGRAM_SIZE - 1; tgt_i >= 0 && !src_done; tgt_i--) + { + tgt_num = tgt_histo[tgt_i].num_counters; + tgt_cum = tgt_histo[tgt_i].cum_value; + /* Keep going until all of the target histogram's counters at this + position have been matched and merged with counters from the + source histogram. */ + while (tgt_num > 0 && !src_done) + { + /* If this is either the first time through this loop or we just + exhausted the previous non-zero source histogram entry, look + for the next non-zero source histogram entry. */ + if (!src_num) + { + /* Locate the next non-zero entry. */ + while (src_i >= 0 && !src_histo[src_i].num_counters) + src_i--; + /* If source histogram has fewer counters, then just copy over the + remaining target counters and quit. */ + if (src_i < 0) + { + tmp_histo[tgt_i].num_counters += tgt_num; + tmp_histo[tgt_i].cum_value += tgt_cum; + if (!tmp_histo[tgt_i].min_value || + tgt_histo[tgt_i].min_value < tmp_histo[tgt_i].min_value) + tmp_histo[tgt_i].min_value = tgt_histo[tgt_i].min_value; + while (--tgt_i >= 0) + { + tmp_histo[tgt_i].num_counters + += tgt_histo[tgt_i].num_counters; + tmp_histo[tgt_i].cum_value += tgt_histo[tgt_i].cum_value; + if (!tmp_histo[tgt_i].min_value || + tgt_histo[tgt_i].min_value + < tmp_histo[tgt_i].min_value) + tmp_histo[tgt_i].min_value = tgt_histo[tgt_i].min_value; + } + + src_done = 1; + break; + } + + src_num = src_histo[src_i].num_counters; + src_cum = src_histo[src_i].cum_value; + } + + /* The number of counters to merge on this pass is the minimum + of the remaining counters from the current target and source + histogram entries. */ + merge_num = tgt_num; + if (src_num < merge_num) + merge_num = src_num; + + /* The merged min_value is the sum of the min_values from target + and source. */ + merge_min = tgt_histo[tgt_i].min_value + src_histo[src_i].min_value; + + /* Compute the portion of source and target entries' cum_value + that will be apportioned to the counters being merged. + The total remaining cum_value from each entry is divided + equally among the counters from that histogram entry if we + are not merging all of them. */ + merge_src_cum = src_cum; + if (merge_num < src_num) + merge_src_cum = merge_num * src_cum / src_num; + merge_tgt_cum = tgt_cum; + if (merge_num < tgt_num) + merge_tgt_cum = merge_num * tgt_cum / tgt_num; + /* The merged cum_value is the sum of the source and target + components. */ + merge_cum = merge_src_cum + merge_tgt_cum; + + /* Update the remaining number of counters and cum_value left + to be merged from this source and target entry. */ + src_cum -= merge_src_cum; + tgt_cum -= merge_tgt_cum; + src_num -= merge_num; + tgt_num -= merge_num; + + /* The merged counters get placed in the new merged histogram + at the entry for the merged min_value. */ + tmp_i = gcov_histo_index (merge_min); + gcov_nonruntime_assert (tmp_i < GCOV_HISTOGRAM_SIZE); + tmp_histo[tmp_i].num_counters += merge_num; + tmp_histo[tmp_i].cum_value += merge_cum; + if (!tmp_histo[tmp_i].min_value || + merge_min < tmp_histo[tmp_i].min_value) + tmp_histo[tmp_i].min_value = merge_min; + + /* Ensure the search for the next non-zero src_histo entry starts + at the next smallest histogram bucket. */ + if (!src_num) + src_i--; + } + } + + gcov_nonruntime_assert (tgt_i < 0); + + /* In the case where there were more counters in the source histogram, + accumulate the remaining unmerged cumulative counter values. Add + those to the smallest non-zero target histogram entry. Otherwise, + the total cumulative counter values in the histogram will be smaller + than the sum_all stored in the summary, which will complicate + computing the working set information from the histogram later on. */ + if (src_num) + src_i--; + while (src_i >= 0) + { + src_cum += src_histo[src_i].cum_value; + src_i--; + } + /* At this point, tmp_i should be the smallest non-zero entry in the + tmp_histo. */ + gcov_nonruntime_assert (tmp_i >= 0 && tmp_i < GCOV_HISTOGRAM_SIZE + && tmp_histo[tmp_i].num_counters > 0); + tmp_histo[tmp_i].cum_value += src_cum; + + /* Finally, copy the merged histogram into tgt_histo. */ + memcpy (tgt_histo, tmp_histo, + sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE); +} +#endif /* !IN_GCOV */ + +/* This is used by gcov-dump (IN_GCOV == -1) and in the compiler + (!IN_GCOV && !IN_LIBGCOV). */ +#if IN_GCOV <= 0 && !IN_LIBGCOV +/* Compute the working set information from the counter histogram in + the profile summary. This is an array of information corresponding to a + range of percentages of the total execution count (sum_all), and includes + the number of counters required to cover that working set percentage and + the minimum counter value in that working set. */ + +GCOV_LINKAGE void +compute_working_sets (const struct gcov_ctr_summary *summary, + gcov_working_set_t *gcov_working_sets) +{ + gcov_type working_set_cum_values[NUM_GCOV_WORKING_SETS]; + gcov_type ws_cum_hotness_incr; + gcov_type cum, tmp_cum; + const gcov_bucket_type *histo_bucket; + unsigned ws_ix, c_num, count; + int h_ix; + + /* Compute the amount of sum_all that the cumulative hotness grows + by in each successive working set entry, which depends on the + number of working set entries. */ + ws_cum_hotness_incr = summary->sum_all / NUM_GCOV_WORKING_SETS; + + /* Next fill in an array of the cumulative hotness values corresponding + to each working set summary entry we are going to compute below. + Skip 0% statistics, which can be extrapolated from the + rest of the summary data. */ + cum = ws_cum_hotness_incr; + for (ws_ix = 0; ws_ix < NUM_GCOV_WORKING_SETS; + ws_ix++, cum += ws_cum_hotness_incr) + working_set_cum_values[ws_ix] = cum; + /* The last summary entry is reserved for (roughly) 99.9% of the + working set. Divide by 1024 so it becomes a shift, which gives + almost exactly 99.9%. */ + working_set_cum_values[NUM_GCOV_WORKING_SETS-1] + = summary->sum_all - summary->sum_all/1024; + + /* Next, walk through the histogram in decending order of hotness + and compute the statistics for the working set summary array. + As histogram entries are accumulated, we check to see which + working set entries have had their expected cum_value reached + and fill them in, walking the working set entries in increasing + size of cum_value. */ + ws_ix = 0; /* The current entry into the working set array. */ + cum = 0; /* The current accumulated counter sum. */ + count = 0; /* The current accumulated count of block counters. */ + for (h_ix = GCOV_HISTOGRAM_SIZE - 1; + h_ix >= 0 && ws_ix < NUM_GCOV_WORKING_SETS; h_ix--) + { + histo_bucket = &summary->histogram[h_ix]; + + /* If we haven't reached the required cumulative counter value for + the current working set percentage, simply accumulate this histogram + entry into the running sums and continue to the next histogram + entry. */ + if (cum + histo_bucket->cum_value < working_set_cum_values[ws_ix]) + { + cum += histo_bucket->cum_value; + count += histo_bucket->num_counters; + continue; + } + + /* If adding the current histogram entry's cumulative counter value + causes us to exceed the current working set size, then estimate + how many of this histogram entry's counter values are required to + reach the working set size, and fill in working set entries + as we reach their expected cumulative value. */ + for (c_num = 0, tmp_cum = cum; + c_num < histo_bucket->num_counters && ws_ix < NUM_GCOV_WORKING_SETS; + c_num++) + { + count++; + /* If we haven't reached the last histogram entry counter, add + in the minimum value again. This will underestimate the + cumulative sum so far, because many of the counter values in this + entry may have been larger than the minimum. We could add in the + average value every time, but that would require an expensive + divide operation. */ + if (c_num + 1 < histo_bucket->num_counters) + tmp_cum += histo_bucket->min_value; + /* If we have reached the last histogram entry counter, then add + in the entire cumulative value. */ + else + tmp_cum = cum + histo_bucket->cum_value; + + /* Next walk through successive working set entries and fill in + the statistics for any whose size we have reached by accumulating + this histogram counter. */ + while (ws_ix < NUM_GCOV_WORKING_SETS + && tmp_cum >= working_set_cum_values[ws_ix]) + { + gcov_working_sets[ws_ix].num_counters = count; + gcov_working_sets[ws_ix].min_counter + = histo_bucket->min_value; + ws_ix++; + } + } + /* Finally, update the running cumulative value since we were + using a temporary above. */ + cum += histo_bucket->cum_value; + } + gcov_nonruntime_assert (ws_ix == NUM_GCOV_WORKING_SETS); +} +#endif /* IN_GCOV <= 0 && !IN_LIBGCOV */ diff --git a/third_party/gcc-linaro-7.5.0-gcov/gcov-io.h b/third_party/gcc-linaro-7.5.0-gcov/gcov-io.h new file mode 100644 index 0000000000..6ce5c7d1dc --- /dev/null +++ b/third_party/gcc-linaro-7.5.0-gcov/gcov-io.h @@ -0,0 +1,425 @@ +/* File format for coverage information + Copyright (C) 1996-2017 Free Software Foundation, Inc. + Contributed by Bob Manson . + Completely remangled by Nathan Sidwell . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +/* Coverage information is held in two files. A notes file, which is + generated by the compiler, and a data file, which is generated by + the program under test. Both files use a similar structure. We do + not attempt to make these files backwards compatible with previous + versions, as you only need coverage information when developing a + program. We do hold version information, so that mismatches can be + detected, and we use a format that allows tools to skip information + they do not understand or are not interested in. + + Numbers are recorded in the 32 bit unsigned binary form of the + endianness of the machine generating the file. 64 bit numbers are + stored as two 32 bit numbers, the low part first. Strings are + padded with 1 to 4 NUL bytes, to bring the length up to a multiple + of 4. The number of 4 bytes is stored, followed by the padded + string. Zero length and NULL strings are simply stored as a length + of zero (they have no trailing NUL or padding). + + int32: byte3 byte2 byte1 byte0 | byte0 byte1 byte2 byte3 + int64: int32:low int32:high + string: int32:0 | int32:length char* char:0 padding + padding: | char:0 | char:0 char:0 | char:0 char:0 char:0 + item: int32 | int64 | string + + The basic format of the files is + + file : int32:magic int32:version int32:stamp record* + + The magic ident is different for the notes and the data files. The + magic ident is used to determine the endianness of the file, when + reading. The version is the same for both files and is derived + from gcc's version number. The stamp value is used to synchronize + note and data files and to synchronize merging within a data + file. It need not be an absolute time stamp, merely a ticker that + increments fast enough and cycles slow enough to distinguish + different compile/run/compile cycles. + + Although the ident and version are formally 32 bit numbers, they + are derived from 4 character ASCII strings. The version number + consists of a two character major version number + (first digit starts from 'A' letter to not to clash with the older + numbering scheme), the single character minor version number, + and a single character indicating the status of the release. + That will be 'e' experimental, 'p' prerelease and 'r' for release. + Because, by good fortune, these are in alphabetical order, string + collating can be used to compare version strings. Be aware that + the 'e' designation will (naturally) be unstable and might be + incompatible with itself. For gcc 17.0 experimental, it would be + 'B70e' (0x42373065). As we currently do not release more than 5 minor + releases, the single character should be always fine. Major number + is currently changed roughly every year, which gives us space + for next 250 years (maximum allowed number would be 259.9). + + A record has a tag, length and variable amount of data. + + record: header data + header: int32:tag int32:length + data: item* + + Records are not nested, but there is a record hierarchy. Tag + numbers reflect this hierarchy. Tags are unique across note and + data files. Some record types have a varying amount of data. The + LENGTH is the number of 4bytes that follow and is usually used to + determine how much data. The tag value is split into 4 8-bit + fields, one for each of four possible levels. The most significant + is allocated first. Unused levels are zero. Active levels are + odd-valued, so that the LSB of the level is one. A sub-level + incorporates the values of its superlevels. This formatting allows + you to determine the tag hierarchy, without understanding the tags + themselves, and is similar to the standard section numbering used + in technical documents. Level values [1..3f] are used for common + tags, values [41..9f] for the notes file and [a1..ff] for the data + file. + + The notes file contains the following records + note: unit function-graph* + unit: header int32:checksum string:source + function-graph: announce_function basic_blocks {arcs | lines}* + announce_function: header int32:ident + int32:lineno_checksum int32:cfg_checksum + string:name string:source int32:lineno + basic_block: header int32:flags* + arcs: header int32:block_no arc* + arc: int32:dest_block int32:flags + lines: header int32:block_no line* + int32:0 string:NULL + line: int32:line_no | int32:0 string:filename + + The BASIC_BLOCK record holds per-bb flags. The number of blocks + can be inferred from its data length. There is one ARCS record per + basic block. The number of arcs from a bb is implicit from the + data length. It enumerates the destination bb and per-arc flags. + There is one LINES record per basic block, it enumerates the source + lines which belong to that basic block. Source file names are + introduced by a line number of 0, following lines are from the new + source file. The initial source file for the function is NULL, but + the current source file should be remembered from one LINES record + to the next. The end of a block is indicated by an empty filename + - this does not reset the current source file. Note there is no + ordering of the ARCS and LINES records: they may be in any order, + interleaved in any manner. The current filename follows the order + the LINES records are stored in the file, *not* the ordering of the + blocks they are for. + + The data file contains the following records. + data: {unit summary:object summary:program* function-data*}* + unit: header int32:checksum + function-data: announce_function present counts + announce_function: header int32:ident + int32:lineno_checksum int32:cfg_checksum + present: header int32:present + counts: header int64:count* + summary: int32:checksum {count-summary}GCOV_COUNTERS_SUMMABLE + count-summary: int32:num int32:runs int64:sum + int64:max int64:sum_max histogram + histogram: {int32:bitvector}8 histogram-buckets* + histogram-buckets: int32:num int64:min int64:sum + + The ANNOUNCE_FUNCTION record is the same as that in the note file, + but without the source location. The COUNTS gives the + counter values for instrumented features. The about the whole + program. The checksum is used for whole program summaries, and + disambiguates different programs which include the same + instrumented object file. There may be several program summaries, + each with a unique checksum. The object summary's checksum is + zero. Note that the data file might contain information from + several runs concatenated, or the data might be merged. + + This file is included by both the compiler, gcov tools and the + runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to + distinguish which case is which. If IN_LIBGCOV is nonzero, + libgcov is being built. If IN_GCOV is nonzero, the gcov tools are + being built. Otherwise the compiler is being built. IN_GCOV may be + positive or negative. If positive, we are compiling a tool that + requires additional functions (see the code for knowledge of what + those functions are). */ + +#ifndef GCC_GCOV_IO_H +#define GCC_GCOV_IO_H + +#ifndef IN_LIBGCOV +/* About the host */ + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if IN_GCOV +// #define GCOV_LINKAGE static +typedef int64_t gcov_type; +typedef uint64_t gcov_type_unsigned; +#if IN_GCOV > 0 +#include +#endif +#endif + +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +#define ATTRIBUTE_HIDDEN + +#endif /* !IN_LIBGOCV */ + +#ifndef GCOV_LINKAGE +#define GCOV_LINKAGE extern +#endif + +#if IN_LIBGCOV +#define gcov_nonruntime_assert(EXPR) ((void)(0 && (EXPR))) +#else +#define gcov_nonruntime_assert(EXPR) gcc_assert (EXPR) +#define gcov_error(...) fatal_error (input_location, __VA_ARGS__) +#endif + +/* File suffixes. */ +#define GCOV_DATA_SUFFIX ".gcda" +#define GCOV_NOTE_SUFFIX ".gcno" + +/* File magic. Must not be palindromes. */ +#define GCOV_DATA_MAGIC ((gcov_unsigned_t)0x67636461) /* "gcda" */ +#define GCOV_NOTE_MAGIC ((gcov_unsigned_t)0x67636e6f) /* "gcno" */ + +/* gcov-iov.h is automatically generated by the makefile from + version.c, it looks like + #define GCOV_VERSION ((gcov_unsigned_t)0x89abcdef) +*/ +#define GCOV_VERSION ((gcov_unsigned_t)0x4137352a) +//#include "gcov-iov.h" + +/* Convert a magic or version number to a 4 character string. */ +#define GCOV_UNSIGNED2STRING(ARRAY,VALUE) \ + ((ARRAY)[0] = (char)((VALUE) >> 24), \ + (ARRAY)[1] = (char)((VALUE) >> 16), \ + (ARRAY)[2] = (char)((VALUE) >> 8), \ + (ARRAY)[3] = (char)((VALUE) >> 0)) + +/* The record tags. Values [1..3f] are for tags which may be in either + file. Values [41..9f] for those in the note file and [a1..ff] for + the data file. The tag value zero is used as an explicit end of + file marker -- it is not required to be present. */ + +#define GCOV_TAG_FUNCTION ((gcov_unsigned_t)0x01000000) +#define GCOV_TAG_FUNCTION_LENGTH (3) +#define GCOV_TAG_BLOCKS ((gcov_unsigned_t)0x01410000) +#define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM) +#define GCOV_TAG_BLOCKS_NUM(LENGTH) (LENGTH) +#define GCOV_TAG_ARCS ((gcov_unsigned_t)0x01430000) +#define GCOV_TAG_ARCS_LENGTH(NUM) (1 + (NUM) * 2) +#define GCOV_TAG_ARCS_NUM(LENGTH) (((LENGTH) - 1) / 2) +#define GCOV_TAG_LINES ((gcov_unsigned_t)0x01450000) +#define GCOV_TAG_COUNTER_BASE ((gcov_unsigned_t)0x01a10000) +#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2) +#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) +#define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) /* Obsolete */ +#define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) +#define GCOV_TAG_SUMMARY_LENGTH(NUM) \ + (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5) +#define GCOV_TAG_AFDO_FILE_NAMES ((gcov_unsigned_t)0xaa000000) +#define GCOV_TAG_AFDO_FUNCTION ((gcov_unsigned_t)0xac000000) +#define GCOV_TAG_AFDO_WORKING_SET ((gcov_unsigned_t)0xaf000000) + + +/* Counters that are collected. */ + +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER, +enum { +#include "gcov-counter.def" +GCOV_COUNTERS +}; +#undef DEF_GCOV_COUNTER + +/* Counters which can be summaried. */ +#define GCOV_COUNTERS_SUMMABLE (GCOV_COUNTER_ARCS + 1) + +/* The first of counters used for value profiling. They must form a + consecutive interval and their order must match the order of + HIST_TYPEs in value-prof.h. */ +#define GCOV_FIRST_VALUE_COUNTER GCOV_COUNTERS_SUMMABLE + +/* The last of counters used for value profiling. */ +#define GCOV_LAST_VALUE_COUNTER (GCOV_COUNTERS - 1) + +/* Number of counters used for value profiling. */ +#define GCOV_N_VALUE_COUNTERS \ + (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1) + +/* The number of hottest callees to be tracked. */ +#define GCOV_ICALL_TOPN_VAL 2 + +/* The number of counter entries per icall callsite. */ +#define GCOV_ICALL_TOPN_NCOUNTS (1 + GCOV_ICALL_TOPN_VAL * 4) + +/* Convert a counter index to a tag. */ +#define GCOV_TAG_FOR_COUNTER(COUNT) \ + (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17)) +/* Convert a tag to a counter. */ +#define GCOV_COUNTER_FOR_TAG(TAG) \ + ((unsigned)(((TAG) - GCOV_TAG_COUNTER_BASE) >> 17)) +/* Check whether a tag is a counter tag. */ +#define GCOV_TAG_IS_COUNTER(TAG) \ + (!((TAG) & 0xFFFF) && GCOV_COUNTER_FOR_TAG (TAG) < GCOV_COUNTERS) + +/* The tag level mask has 1's in the position of the inner levels, & + the lsb of the current level, and zero on the current and outer + levels. */ +#define GCOV_TAG_MASK(TAG) (((TAG) - 1) ^ (TAG)) + +/* Return nonzero if SUB is an immediate subtag of TAG. */ +#define GCOV_TAG_IS_SUBTAG(TAG,SUB) \ + (GCOV_TAG_MASK (TAG) >> 8 == GCOV_TAG_MASK (SUB) \ + && !(((SUB) ^ (TAG)) & ~GCOV_TAG_MASK (TAG))) + +/* Return nonzero if SUB is at a sublevel to TAG. */ +#define GCOV_TAG_IS_SUBLEVEL(TAG,SUB) \ + (GCOV_TAG_MASK (TAG) > GCOV_TAG_MASK (SUB)) + +/* Basic block flags. */ +#define GCOV_BLOCK_UNEXPECTED (1 << 1) + +/* Arc flags. */ +#define GCOV_ARC_ON_TREE (1 << 0) +#define GCOV_ARC_FAKE (1 << 1) +#define GCOV_ARC_FALLTHROUGH (1 << 2) + +/* Structured records. */ + +/* Structure used for each bucket of the log2 histogram of counter values. */ +typedef struct +{ + /* Number of counters whose profile count falls within the bucket. */ + gcov_unsigned_t num_counters; + /* Smallest profile count included in this bucket. */ + gcov_type min_value; + /* Cumulative value of the profile counts in this bucket. */ + gcov_type cum_value; +} gcov_bucket_type; + +/* For a log2 scale histogram with each range split into 4 + linear sub-ranges, there will be at most 64 (max gcov_type bit size) - 1 log2 + ranges since the lowest 2 log2 values share the lowest 4 linear + sub-range (values 0 - 3). This is 252 total entries (63*4). */ + +#define GCOV_HISTOGRAM_SIZE 252 + +/* How many unsigned ints are required to hold a bit vector of non-zero + histogram entries when the histogram is written to the gcov file. + This is essentially a ceiling divide by 32 bits. */ +#define GCOV_HISTOGRAM_BITVECTOR_SIZE (GCOV_HISTOGRAM_SIZE + 31) / 32 + +/* Cumulative counter data. */ +struct gcov_ctr_summary +{ + gcov_unsigned_t num; /* number of counters. */ + gcov_unsigned_t runs; /* number of program runs */ + gcov_type sum_all; /* sum of all counters accumulated. */ + gcov_type run_max; /* maximum value on a single run. */ + gcov_type sum_max; /* sum of individual run max values. */ + gcov_bucket_type histogram[GCOV_HISTOGRAM_SIZE]; /* histogram of + counter values. */ +}; + +/* Object & program summary record. */ +struct gcov_summary +{ + gcov_unsigned_t checksum; /* checksum of program */ + struct gcov_ctr_summary ctrs[GCOV_COUNTERS_SUMMABLE]; +}; + +#if !defined(inhibit_libc) + +/* Functions for reading and writing gcov files. In libgcov you can + open the file for reading then writing. Elsewhere you can open the + file either for reading or for writing. When reading a file you may + use the gcov_read_* functions, gcov_sync, gcov_position, & + gcov_error. When writing a file you may use the gcov_write + functions, gcov_seek & gcov_error. When a file is to be rewritten + you use the functions for reading, then gcov_rewrite then the + functions for writing. Your file may become corrupted if you break + these invariants. */ + +#if !IN_LIBGCOV +GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/); +GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t); +#endif + +/* Available everywhere. */ +GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE const char *gcov_read_string (void); +GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/, + gcov_unsigned_t /*length */); + +#if !IN_GCOV +/* Available outside gcov */ +GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN; +#endif + +#if !IN_GCOV && !IN_LIBGCOV +/* Available only in compiler */ +GCOV_LINKAGE unsigned gcov_histo_index (gcov_type value); +GCOV_LINKAGE void gcov_write_string (const char *); +GCOV_LINKAGE gcov_position_t gcov_write_tag (gcov_unsigned_t); +GCOV_LINKAGE void gcov_write_length (gcov_position_t /*position*/); +#endif + +#if IN_GCOV <= 0 && !IN_LIBGCOV +/* Available in gcov-dump and the compiler. */ + +/* Number of data points in the working set summary array. Using 128 + provides information for at least every 1% increment of the total + profile size. The last entry is hardwired to 99.9% of the total. */ +#define NUM_GCOV_WORKING_SETS 128 + +/* Working set size statistics for a given percentage of the entire + profile (sum_all from the counter summary). */ +typedef struct gcov_working_set_info +{ + /* Number of hot counters included in this working set. */ + unsigned num_counters; + /* Smallest counter included in this working set. */ + gcov_type min_counter; +} gcov_working_set_t; + +GCOV_LINKAGE void compute_working_sets (const struct gcov_ctr_summary *summary, + gcov_working_set_t *gcov_working_sets); +#endif + +#if IN_GCOV > 0 +/* Available in gcov */ +GCOV_LINKAGE time_t gcov_time (void); +#endif + +#endif /* !inhibit_libc */ + +#endif /* GCC_GCOV_IO_H */ -- Gitee