Signed-off-by: sunshuaiyuan <sunshuaiyuan@huawei.com>
This commit is contained in:
sunshuaiyuan 2023-06-06 17:22:53 +08:00
parent ca089319e4
commit b47f5baede
6 changed files with 398 additions and 20 deletions

View File

@ -388,11 +388,38 @@ public:
{
clockId_ = clockId;
};
void SetPerCpu(bool perCpu);
void SetPerThread(bool perThread);
bool SetBranchSampleType(uint64_t value);
bool AddDefaultEvent(perf_type_id type);
std::map<__u64, std::string> GetSupportEvents(perf_type_id type);
struct Summary {
int cpu;
pid_t tid;
__u64 eventCount = 0;
__u64 time_enabled = 0;
__u64 time_running = 0;
Summary(const int cpu, const pid_t tid, const __u64 eventCount,
const __u64 time_enabled, const __u64 time_running)
: cpu(cpu), tid(tid), eventCount(eventCount), time_enabled(time_enabled), time_running(time_running)
{
}
};
struct ReportSum {
int cpu;
pid_t pid;
pid_t tid;
double scaleSum = 1.0;
double commentSum = 0;
__u64 eventCountSum = 0;
__u64 enabledSum = 0;
__u64 runningSum = 0;
std::string configName = "";
std::string threadName = "";
};
struct CountEvent {
bool userOnly = false;
bool kernelOnly = false;
@ -401,6 +428,7 @@ public:
__u64 time_running = 0;
__u64 id = 0;
double used_cpus = 0;
std::vector<Summary> summaries;
};
using StatCallBack =
std::function<void(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &)>;
@ -516,6 +544,8 @@ private:
std::vector<OHOS::UniqueFd> groups_;
std::chrono::milliseconds timeOut_; // milliseconds
std::chrono::milliseconds timeReport_; // means same as timeOut
bool perCpu_ = false;
bool perThread_ = false;
bool verboseReport_ = false;
bool prepared_ = false;
ConfigTable traceConfigTable;

View File

@ -71,6 +71,10 @@ public:
" --chkms <millisec>\n"
" Set the interval of querying the <package_name>.\n"
" <millisec> is in range [1-200], default is 10.\n"
" --per-core\n"
" Print counters for each cpu core.\n"
" --per-thread\n"
" Print counters for each thread.\n"
" --restart\n"
" Collect performance counter information of application startup.\n"
" Record will exit if the process is not started within 30 seconds.\n"
@ -84,6 +88,7 @@ public:
bool OnSubCommand(std::vector<std::string> &args) override;
bool ParseOption(std::vector<std::string> &args) override;
bool ParseSpecialOption(std::vector<std::string> &args);
void DumpOptions(void) const override;
private:
@ -100,6 +105,8 @@ private:
int checkAppMs_ = DEFAULT_CHECK_APP_MS;
std::vector<pid_t> selectPids_;
std::vector<pid_t> selectTids_;
bool perCpus_ {false};
bool perThreads_ {false};
bool verboseReport_ {false};
std::vector<std::string> trackedCommand_ {};
bool helpOption_ {false};
@ -118,7 +125,22 @@ private:
static std::string GetCommentConfigName(
const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName);
static void Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &);
static void Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents);
static void PrintPerHead();
static void GetPerKey(std::string &perKey, const PerfEvents::Summary &summary);
static void FormatComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr);
static void ReportNormal(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents);
static void ReportDetailInfos(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents);
static void PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio,
std::string &configName);
static void InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap,
const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance);
static bool FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale);
static bool FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_in_sec, double &main_scale);
static std::string GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent, double &comment,
PerfEvents::Summary &summary, std::string &configName);
static std::string HandleOtherConfig(double &comment, PerfEvents::Summary &summary,
double running_time_in_sec, double scale, bool findRunningTime);
void PrintUsage();
inline bool HelpOption()
@ -128,6 +150,7 @@ private:
bool PrepairEvents();
bool CheckOptions(const std::vector<pid_t> &pids);
bool CheckSelectCpuPidOption();
void SetReportFlags(bool cpuFlag, bool threadFlag);
};
bool RegisterSubCommandStat(void);

View File

@ -98,6 +98,10 @@ static const std::string USER_TYPE_PARAM_GET = "";
static FILE *outputDump_ = nullptr;
const uint64_t waitAppRunCheckTimeOut = 10;
struct ThreadInfos {
pid_t tid;
pid_t pid;
};
// string function
class MemoryHold {
public:
@ -206,6 +210,7 @@ std::string StringPrintf(const char *stringFormat, VA... args)
std::vector<std::string> GetEntriesInDir(const std::string &basePath);
std::vector<std::string> GetSubDirs(const std::string &basePath);
std::vector<pid_t> GetSubthreadIDs(const pid_t pid, std::map<pid_t, ThreadInfos> &thread_map);
bool IsDir(const std::string &path);
@ -319,6 +324,8 @@ bool PowerOfTwo(uint64_t n);
#endif
pid_t GetAppPackagePid(const std::string &appPackage, const pid_t oldPid, const int checkAppMs,
const uint64_t waitAppTimeOut);
bool IsNeedCheckSamePid(const std::string &fileName, const std::string &appPackage, const std::string &subDir,
pid_t &res, const pid_t oldPid);
bool CheckAppIsRunning (std::vector<pid_t> &selectPids, const std::string &appPackage, int checkAppMs);
bool IsExistDebugByApp(const std::string& bundleName);
bool IsExistDebugByPid(const std::vector<pid_t> pids);

View File

@ -719,6 +719,16 @@ void PerfEvents::LoadTracepointEventTypesFromSystem()
}
}
void PerfEvents::SetPerCpu(bool perCpu)
{
perCpu_ = perCpu;
}
void PerfEvents::SetPerThread(bool perThread)
{
perThread_ = perThread;
}
void PerfEvents::SetVerboseReport(bool verboseReport)
{
verboseReport_ = verboseReport;
@ -829,7 +839,10 @@ bool PerfEvents::PrepareFdEvents(void)
pids_.push_back(0); // no pid means use 0 as self pid
}
}
if (perCpu_ || perThread_) {
cpus_.clear();
PutAllCpus();
}
if (cpus_.empty()) {
PutAllCpus();
}
@ -1024,6 +1037,10 @@ bool PerfEvents::StatReport(const __u64 &durationInSec)
fditem.pid, readNoGroupValue.time_enabled,
readNoGroupValue.time_running, readNoGroupValue.value);
}
if ((perCpu_ || perThread_) && readNoGroupValue.value) {
countEvent->summaries.emplace_back(fditem.cpu, fditem.pid, readNoGroupValue.value,
readNoGroupValue.time_enabled, readNoGroupValue.time_running);
}
} else {
printf("read failed from event '%s'\n", eventItem.configName.c_str());
}

View File

@ -29,6 +29,10 @@ const uint16_t THOUSNADS_SEPARATOR = 3;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
static std::map<pid_t, ThreadInfos> thread_map_;
static bool g_reportCpuFlag = false;
static bool g_reportThreadFlag = false;
static VirtualRuntime runtimeInstance_;
void SubCommandStat::DumpOptions() const
{
printf("DumpOptions:\n");
@ -44,6 +48,8 @@ void SubCommandStat::DumpOptions() const
printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
printf(" restart:\t%s\n", restart_ ? "true" : "false");
printf(" perCore:\t%s\n", perCpus_ ? "true" : "false");
printf(" perTread:\t%s\n", perThreads_ ? "true" : "false");
printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
}
@ -113,10 +119,23 @@ bool SubCommandStat::ParseOption(std::vector<std::string> &args)
HLOGD("get option --restart failed");
return false;
}
if (!Option::GetOptionValue(args, "--per-core", perCpus_)) {
HLOGD("get option --per-core failed");
return false;
}
if (!Option::GetOptionValue(args, "--per-thread", perThreads_)) {
HLOGD("get option --per-thread failed");
return false;
}
if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
HLOGD("get option --verbose failed");
return false;
}
return ParseSpecialOption(args);
}
bool SubCommandStat::ParseSpecialOption(std::vector<std::string> &args)
{
if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) {
HLOGD("get cmd failed");
return false;
@ -133,19 +152,146 @@ void SubCommandStat::PrintUsage()
printf("%s\n", Help().c_str());
}
void SubCommandStat::Report(
void SubCommandStat::SetReportFlags(bool cpuFlag, bool threadFlag)
{
g_reportCpuFlag = cpuFlag;
g_reportThreadFlag = threadFlag;
}
void SubCommandStat::Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
{
bool isNeedPerCpuTid = false;
for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
if (!(it->second->summaries.empty())) {
isNeedPerCpuTid = true;
break;
}
}
if (isNeedPerCpuTid) {
PrintPerHead();
ReportDetailInfos(countEvents);
} else {
ReportNormal(countEvents);
}
}
void SubCommandStat::PrintPerHead()
{
// print head
if (g_reportCpuFlag && g_reportThreadFlag) {
printf(" %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name",
"pid", "tid", "coreid", "comment", "coverage");
return;
}
if (g_reportCpuFlag) {
printf(" %24s %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage");
return;
}
printf(" %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid",
"comment", "coverage");
return;
}
void SubCommandStat::PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio,
std::string &configName)
{
// print value
std::string strEventCount = std::to_string(reportSum->eventCountSum);
for (size_t i = strEventCount.size() - 1, j = 1; i > 0; --i, ++j) {
if (j == THOUSNADS_SEPARATOR) {
strEventCount.insert(strEventCount.begin() + i, ',');
j = 0;
}
}
std::string commentStr;
FormatComments(reportSum, commentStr);
if (g_reportCpuFlag && g_reportThreadFlag) {
printf(" %24s %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, reportSum->cpu, commentStr.c_str(),
reportSum->scaleSum * ratio);
} else if (g_reportCpuFlag) {
printf(" %24s %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
} else {
printf(" %24s %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, commentStr.c_str(),
reportSum->scaleSum * ratio);
}
fflush(stdout);
}
void SubCommandStat::InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap,
const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance)
{
newPerMap->cpu = summary.cpu;
if (g_reportCpuFlag && !g_reportThreadFlag) {
return;
}
newPerMap->tid = summary.tid;
newPerMap->pid = thread_map_.find(summary.tid)->second.pid;
bool isTid = true;
if (newPerMap->pid == newPerMap->tid) {
isTid = false;
}
newPerMap->threadName = virtualInstance.ReadThreadName(summary.tid, isTid);
}
void SubCommandStat::GetPerKey(std::string &perKey, const PerfEvents::Summary &summary)
{
perKey = "";
if (g_reportCpuFlag) {
perKey += std::to_string(summary.cpu);
}
if (g_reportThreadFlag) {
perKey += std::to_string(summary.tid);
}
return;
}
void SubCommandStat::ReportDetailInfos(
const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
{
// head
printf(" %24s %-30s | %-32s | %s\n", "count", "name", "comment", "coverage");
std::string perKey = "";
std::map<std::string, std::unique_ptr<PerfEvents::ReportSum>> perMaps;
for (auto event = countEvents.begin(); event != countEvents.end(); ++event) {
if (event->second->eventCount == 0) {
continue;
}
double scale = 1.0;
constexpr float ratio {100.0};
std::string configName = event->first;
perMaps.clear();
for (auto &it : event->second->summaries) {
GetPerKey(perKey, it);
if (perMaps.count(perKey) == 0) {
auto perMap = std::make_unique<PerfEvents::ReportSum>(PerfEvents::ReportSum {});
InitPerMap(perMap, it, runtimeInstance_);
perMaps[perKey] = std::move(perMap);
}
perMaps[perKey]->configName = GetDetailComments(event->second, perMaps[perKey]->commentSum,
it, configName);
perMaps[perKey]->eventCountSum += it.eventCount;
if (it.time_running < it.time_enabled && it.time_running != 0) {
perMaps[perKey]->scaleSum += 1 / (static_cast<double>(it.time_enabled) / it.time_running);
}
}
for (auto iper = perMaps.begin(); iper != perMaps.end(); iper++) {
PrintPerValue(iper->second, ratio, configName);
}
}
}
void SubCommandStat::ReportNormal(
const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
{
// print head
std::map<std::string, std::string> comments;
GetComments(countEvents, comments);
for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
double scale = 1.0;
constexpr float ratio {100.0};
std::string configName = it->first;
std::string comment = comments[configName];
constexpr float ratio {100.0};
std::string strEventCount = std::to_string(it->second->eventCount);
for (size_t i = strEventCount.size() - 1, j = 1; i > 0; --i, ++j) {
if (j == THOUSNADS_SEPARATOR) {
@ -163,8 +309,7 @@ void SubCommandStat::Report(
}
}
bool SubCommandStat::FindEventCount(
const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
bool SubCommandStat::FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale)
{
auto itr = countEvents.find(configName);
@ -180,6 +325,16 @@ bool SubCommandStat::FindEventCount(
return false;
}
bool SubCommandStat::FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale)
{
eventCount = summary.eventCount;
if (summary.time_running < summary.time_enabled && summary.time_running != 0) {
scale = static_cast<double>(summary.time_enabled) / summary.time_running;
return true;
}
return false;
}
std::string SubCommandStat::GetCommentConfigName(
const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName)
{
@ -197,6 +352,109 @@ std::string SubCommandStat::GetCommentConfigName(
return commentConfigName;
}
void SubCommandStat::FormatComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr)
{
if (reportSum->commentSum == 0) {
return;
}
if (reportSum->configName == "sw-task-clock") {
commentStr = StringPrintf("%lf cpus used", reportSum->commentSum);
return;
}
if (reportSum->configName == "hw-cpu-cycles") {
commentStr = StringPrintf("%lf GHz", reportSum->commentSum);
return;
}
if (reportSum->configName == "hw-instructions") {
commentStr = StringPrintf("%lf cycles per instruction", reportSum->commentSum);
return;
}
if (reportSum->configName == "hw-branch-misses") {
commentStr = StringPrintf("%lf miss rate", reportSum->commentSum);
return;
}
if (reportSum->commentSum > 1e9) {
commentStr = StringPrintf("%.3lf G/sec", reportSum->commentSum / 1e9);
return;
}
if (reportSum->commentSum > 1e6) {
commentStr = StringPrintf("%.3lf M/sec", reportSum->commentSum / 1e6);
return;
}
if (reportSum->commentSum > 1e3) {
commentStr = StringPrintf("%.3lf K/sec", reportSum->commentSum / 1e3);
return;
}
commentStr = StringPrintf("%.3lf /sec", reportSum->commentSum);
}
std::string SubCommandStat::GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent,
double &comment, PerfEvents::Summary &summary, std::string &configName)
{
double running_time_in_sec = 0;
double main_scale = 1.0;
bool findRunningTime = FindPercoreRunningTime(summary, running_time_in_sec, main_scale);
if (configName == GetCommentConfigName(countEvent, "sw-cpu-clock")) {
comment = 0;
return "sw-cpu-clock";
}
double scale = 1.0;
if (summary.time_running < summary.time_enabled && summary.time_running != 0) {
scale = static_cast<double>(summary.time_enabled) / summary.time_running;
}
if (configName == GetCommentConfigName(countEvent, "sw-task-clock")) {
comment += countEvent->used_cpus * scale;
return "sw-task-clock";
}
if (configName == GetCommentConfigName(countEvent, "hw-cpu-cycles")) {
if (findRunningTime) {
double hz = 0;
if (running_time_in_sec != 0) {
hz = summary.eventCount / (running_time_in_sec / scale);
}
comment += hz / 1e9;
} else {
comment += 0;
}
return "hw-cpu-cycles";
}
if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
double otherScale = 1.0;
__u64 cpuCyclesCount = 0;
bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
return "hw-instructions";
}
}
if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
double otherScale = 1.0;
__u64 branchInstructionsCount = 0;
bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
branchInstructionsCount != 0) {
comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
return "hw-branch-misses";
}
}
return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
}
std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec,
double scale, bool findRunningTime)
{
comment = 0;
if (findRunningTime) {
double rate = 0;
if (scale != 0) {
rate = summary.eventCount / (running_time_in_sec / scale);
}
comment += rate;
}
return "";
}
bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
{
constexpr double SCALE_ERROR_LIMIT = 1e-5;
@ -314,6 +572,19 @@ bool SubCommandStat::FindRunningTime(
return false;
}
bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
double &main_scale)
{
if (summary.eventCount == 0) {
return false;
}
running_time_int_sec = summary.eventCount / 1e9;
if (summary.time_running < summary.time_enabled && summary.time_running != 0) {
main_scale = static_cast<double>(summary.time_enabled) /summary.time_running;
}
return true;
}
bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids)
{
if (!CheckOptionPid(pids)) {
@ -361,7 +632,7 @@ bool SubCommandStat::OnSubCommand(std::vector<std::string> &args)
std::vector<pid_t> pids;
for (auto selectPid : selectPids_) {
pids.push_back(selectPid);
std::vector<pid_t> subTids = GetSubthreadIDs(selectPid);
std::vector<pid_t> subTids = GetSubthreadIDs(selectPid, thread_map_);
if (!subTids.empty()) {
pids.insert(pids.end(), subTids.begin(), subTids.end());
}
@ -376,9 +647,12 @@ bool SubCommandStat::OnSubCommand(std::vector<std::string> &args)
HLOGV("CheckOptionPidAndApp() failed");
return false;
}
SetReportFlags(perCpus_, perThreads_);
perfEvents_.SetSystemTarget(targetSystemWide_);
perfEvents_.SetTimeOut(timeStopSec_);
perfEvents_.SetTimeReport(timeReportMs_);
perfEvents_.SetPerCpu(perCpus_);
perfEvents_.SetPerThread(perThreads_);
perfEvents_.SetVerboseReport(verboseReport_);
perfEvents_.SetInherit(!noCreateNew_);
perfEvents_.SetTrackedCommand(trackedCommand_);

View File

@ -522,6 +522,24 @@ std::vector<pid_t> GetSubthreadIDs(const pid_t pid)
return res;
}
std::vector<pid_t> GetSubthreadIDs(const pid_t pid, std::map<pid_t, ThreadInfos> &thread_map)
{
std::string path {"/proc/"};
path += std::to_string(pid);
path += "/task/";
auto tids = GetSubDirs(path);
std::vector<pid_t> res{};
for (auto tidStr : tids) {
ThreadInfos info;
pid_t tid = static_cast<pid_t>(std::stoul(tidStr, nullptr));
info.tid = tid;
info.pid = pid;
thread_map[tid] = info;
res.push_back(tid);
}
return res;
}
bool StringStartsWith(const std::string &string, const std::string &with)
{
return string.find(with) == 0;
@ -588,16 +606,8 @@ pid_t GetAppPackagePid(const std::string &appPackage, const pid_t oldPid, const
}
std::string fileName {basePath + subDir};
fileName += "/cmdline";
if (IsSameCommand(ReadFileToString(fileName), appPackage)) {
res = std::stoul(subDir, nullptr);
if (res == oldPid) {
res = -1;
continue;
}
if (res >= 0) {
HLOGD("[GetAppPackagePid]: get appid for %s is %d", appPackage.c_str(), res);
return res;
}
if (!IsNeedCheckSamePid(fileName, appPackage, subDir, res, oldPid)) {
return res;
}
}
std::this_thread::sleep_for(milliseconds(checkAppMs));
@ -606,6 +616,23 @@ pid_t GetAppPackagePid(const std::string &appPackage, const pid_t oldPid, const
return res;
}
bool IsNeedCheckSamePid(const std::string &fileName, const std::string &appPackage, const std::string &subDir,
pid_t &res, const pid_t oldPid)
{
if (IsSameCommand(ReadFileToString(fileName), appPackage)) {
res = std::stoul(subDir, nullptr);
if (res == oldPid) {
res = -1;
return true;
}
if (res >= 0) {
HLOGD("[GetAppPackagePid]: get appid for %s is %d", appPackage.c_str(), res);
return false;
}
}
return true;
}
bool CheckAppIsRunning (std::vector<pid_t> &selectPids, const std::string &appPackage, int checkAppMs)
{
if (!appPackage.empty()) {