diff --git a/BUILD.gn b/BUILD.gn index 9a3d49b..e6ab916 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -340,6 +340,7 @@ ohos_executable("hiperf") { install_enable = true sources = [ "./src/main.cpp" ] deps = [ + ":hiperf_etc", ":hiperf_platform_common", ":hiperf_platform_linux", ] @@ -369,6 +370,43 @@ ohos_executable("hiperf_host") { part_name = "hiperf" } +ohos_prebuilt_etc("hiperf.para") { + source = "etc/hiperf.para" + install_images = [ + "system", + "updater", + ] + module_install_dir = "etc/param" + part_name = "hiperf" + subsystem_name = "developtools" +} + +ohos_prebuilt_etc("hiperf.para.dac") { + source = "etc/hiperf.para.dac" + install_images = [ + "system", + "updater", + ] + module_install_dir = "etc/param" + part_name = "hiperf" + subsystem_name = "developtools" +} + +ohos_prebuilt_etc("hiperf.cfg") { + source = "etc/hiperf.cfg" + relative_install_dir = "init" + subsystem_name = "developtools" + part_name = "hiperf" +} + +group("hiperf_etc") { + deps = [ + ":hiperf.cfg", + ":hiperf.para", + ":hiperf.para.dac", + ] +} + ohos_source_set("hiperf_platform_host") { part_name = "hiperf" sources = [ "./src/hiperf_libreport.cpp" ] diff --git a/etc/hiperf.cfg b/etc/hiperf.cfg new file mode 100644 index 0000000..34a1b65 --- /dev/null +++ b/etc/hiperf.cfg @@ -0,0 +1,18 @@ +{ + "jobs": [{ + "name": "param:security.perf_harden=0", + "condition": "security.perf_harden=0", + "cmds": [ + "write /proc/sys/kernel/perf_event_max_sample_rate ${hiviewdfx.hiperf.perf_event_max_sample_rate}", + "write /proc/sys/kernel/perf_cpu_time_max_percent ${hiviewdfx.hiperf.perf_cpu_time_max_percent}", + "write /proc/sys/kernel/perf_event_mlock_kb ${hiviewdfx.hiperf.perf_event_mlock_kb}" + ] + }, { + "name": "post-init", + "cmds": [ + "setparam hiviewdfx.hiperf.perf_event_max_sample_rate 100000", + "setparam hiviewdfx.hiperf.perf_cpu_time_max_percent 25", + "setparam hiviewdfx.hiperf.perf_event_mlock_kb 516" + ] + }] +} \ No newline at end of file diff --git a/etc/hiperf.para b/etc/hiperf.para new file mode 100644 index 0000000..e18d867 --- /dev/null +++ b/etc/hiperf.para @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +hiviewdfx.hiperf.perf_event_max_sample_rate=100000 +hiviewdfx.hiperf.perf_cpu_time_max_percent=25 +hiviewdfx.hiperf.perf_event_mlock_kb=516 \ No newline at end of file diff --git a/etc/hiperf.para.dac b/etc/hiperf.para.dac new file mode 100644 index 0000000..d43b489 --- /dev/null +++ b/etc/hiperf.para.dac @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +hiviewdfx.hiperf.perf_event_max_sample_rate=root:shell:0775 +hiviewdfx.hiperf.perf_cpu_time_max_percent=root:shell:0775 +hiviewdfx.hiperf.perf_event_mlock_kb=root:shell:0775 \ No newline at end of file diff --git a/include/perf_events.h b/include/perf_events.h index 4544a25..13b7147 100644 --- a/include/perf_events.h +++ b/include/perf_events.h @@ -205,23 +205,6 @@ struct read_format_no_group { __u64 id; /* if PERF_FORMAT_ID */ }; -/* -2 allow only user-space measurements (default since - Linux 4.6). -1 allow both kernel and user measurements (default - before Linux 4.6). -0 allow access to CPU-specific data but not raw - tracepoint samples. --1 no restrictions. -*/ -enum PerfEventParanoid { - NOLIMIT = -1, - KERNEL_USER_CPU = 0, - KERNEL_USER = 1, - USER = 2, - UNKNOW = 99, -}; - class PerfEvents { public: static constexpr uint64_t DEFAULT_SAMPLE_FREQUNCY = 4000; @@ -411,12 +394,6 @@ private: }; uint8_t eventSpaceType_ = EventSpaceType::UNKNOW; - PerfEventParanoid requestPermission_ = PerfEventParanoid::USER; - bool CheckPermissions(PerfEventParanoid request = KERNEL_USER_CPU); - bool CheckOhosPermissions(); - - static PerfEventParanoid perfEventParanoid_; - bool inherit_ = false; std::vector pids_; std::vector cpus_; diff --git a/include/subcommand_record.h b/include/subcommand_record.h index 4a76f91..53d3c2f 100644 --- a/include/subcommand_record.h +++ b/include/subcommand_record.h @@ -245,6 +245,9 @@ private: size_t recordSamples_ = 0; size_t recordNoSamples_ = 0; + + bool isNeedSetPerfHarden_ = false; + // callback to process record bool ProcessRecord(std::unique_ptr); bool SaveRecord(std::unique_ptr); @@ -271,8 +274,11 @@ private: bool CollectionSymbol(std::unique_ptr record); + bool SetPerfLimit(const std::string& file, const std::string& param, int value); bool SetPerfCpuMaxPercent(); bool SetPerfMaxSampleRate(); + bool SetPerfEventMlock(); + bool SetPerfHarden(); bool TraceOffCpu(); bool ParseCallStackOption(const std::vector &callStackType); diff --git a/src/perf_events.cpp b/src/perf_events.cpp index 218ee58..a997c4a 100644 --- a/src/perf_events.cpp +++ b/src/perf_events.cpp @@ -45,17 +45,13 @@ static std::atomic_bool g_trackRunning = false; OHOS::UniqueFd PerfEvents::Open(perf_event_attr &attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { - if (perfEventParanoid_ >= PerfEventParanoid::USER) { - attr.exclude_kernel = true; // kernel restrict - } OHOS::UniqueFd fd = UniqueFd(syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, flags)); if (fd < 0) { HLOGEP("syscall perf_event_open failed. "); // dump when open failed. SubCommandDump::DumpPrintEventAttr(attr, std::numeric_limits::min()); } - HLOGV("perf_event_open: got fd %d for pid %d cpu %d group %d flags %lu perfEventParanoid %d", - fd.Get(), pid, cpu, group_fd, flags, perfEventParanoid_); + HLOGV("perf_event_open: got fd %d for pid %d cpu %d group %d flags %lu", fd.Get(), pid, cpu, group_fd, flags); return fd; } @@ -87,73 +83,6 @@ PerfEvents::~PerfEvents() ExitReadRecordBufThread(); } -PerfEventParanoid PerfEvents::perfEventParanoid_ = PerfEventParanoid::UNKNOW; - -bool PerfEvents::CheckOhosPermissions() -{ -#if defined(CONFIG_HAS_SYSPARA) - std::string perfHarden = "0"; - perfHarden = OHOS::system::GetParameter(PERF_DISABLE_PARAM, perfHarden); - HLOGD("%s is %s", PERF_DISABLE_PARAM.c_str(), perfHarden.c_str()); - if (perfHarden == "1") { - printf("param '%s' is disabled, try to enable it\n", PERF_DISABLE_PARAM.c_str()); - // we will try to set it as 0 - perfHarden = OHOS::system::SetParameter(PERF_DISABLE_PARAM, "0"); - // wait init config the param - std::this_thread::sleep_for(1s); - if (OHOS::system::GetParameter(PERF_DISABLE_PARAM, perfHarden) == "1") { - printf("setparam failed. pls try setparam %s 0\n", PERF_DISABLE_PARAM.c_str()); - } - } - return perfHarden == "0"; -#else - return true; // not ohos -#endif -} - -bool PerfEvents::CheckPermissions(PerfEventParanoid request) -{ - // check the ohos param "security.perf_harden" - - if (getuid() == 0) { - // we are root perfEventParanoid as -1 - perfEventParanoid_ = PerfEventParanoid::NOLIMIT; - printf("this is root mode, perfEventParanoid assume as -1\n"); - return true; - } - - std::string perfEventParanoid = ReadFileToString(PERF_EVENT_PARANOID); - if (perfEventParanoid.empty()) { - printf("unable to read %s, assume as 2\n", PERF_EVENT_PARANOID.c_str()); - perfEventParanoid_ = PerfEventParanoid::USER; - } else { - perfEventParanoid_ = static_cast(stoi(perfEventParanoid)); - } - -#if is_ohos - // not root and in ohos - if (!CheckOhosPermissions()) { - return false; - } -#endif - - if (perfEventParanoid_ == PerfEventParanoid::NOLIMIT) { - return true; - } - printf("%s is %d\n", PERF_EVENT_PARANOID.c_str(), perfEventParanoid_); - if (perfEventParanoid_ >= PerfEventParanoid::USER) { - printf("allow only user-space measurements (default since Linux 4.6).\n"); - } else if (perfEventParanoid_ == PerfEventParanoid::KERNEL_USER) { - printf("allow both kernel and user measurements (default before Linux 4.6).\n"); - } else if (perfEventParanoid_ == PerfEventParanoid::KERNEL_USER_CPU) { - printf("allow access to CPU-specific data but not raw tracepoint samples.\n"); - } else if (perfEventParanoid_ <= PerfEventParanoid::NOLIMIT) { - printf("unable to read anything\n"); - } - printf("request level is %d\n", request); - return perfEventParanoid_ <= request; -} - bool PerfEvents::IsEventSupport(perf_type_id type, __u64 config) { HLOGV("enter"); @@ -302,10 +231,6 @@ bool PerfEvents::AddEvent(const std::string &eventString, bool followGroup) return false; } if (excludeUser) { - if (requestPermission_ > PerfEventParanoid::KERNEL_USER) { - requestPermission_ = PerfEventParanoid::KERNEL_USER; - } - eventSpaceType_ |= EventSpaceType::KERNEL; } else if (excludeKernel) { eventSpaceType_ |= EventSpaceType::USER; @@ -497,11 +422,6 @@ static void RecoverCaptureSig() bool PerfEvents::PrepareTracking(void) { HLOGV("enter"); - - if (!CheckPermissions(requestPermission_)) { - return false; - } - // 1. prepare cpu pid if (!PrepareFdEvents()) { HLOGE("PrepareFdEvents() failed"); @@ -514,6 +434,7 @@ bool PerfEvents::PrepareTracking(void) return false; } + HLOGV("success"); prepared_ = true; return true; } @@ -581,7 +502,9 @@ void PerfEvents::WaitRecordThread() bool PerfEvents::StartTracking(bool immediately) { + HLOGV("enter"); if (!prepared_) { + HLOGD("do not prepared_"); return false; } @@ -713,12 +636,6 @@ void PerfEvents::SetSystemTarget(bool systemTarget) void PerfEvents::SetCpu(std::vector cpus) { cpus_ = cpus; - - if (!cpus_.empty()) { - if (requestPermission_ > PerfEventParanoid::KERNEL_USER_CPU) { - requestPermission_ = PerfEventParanoid::KERNEL_USER_CPU; - } - } } void PerfEvents::SetPid(std::vector pids) @@ -907,10 +824,6 @@ bool PerfEvents::PrepareFdEvents(void) if (systemTarget_) { pids_.clear(); pids_.push_back(-1); - - if (cpus_.empty()) { - PutAllCpus(); - } } else { if (trackedCommand_) { pids_.push_back(trackedCommand_->GetChildPid()); @@ -918,15 +831,10 @@ bool PerfEvents::PrepareFdEvents(void) if (pids_.empty()) { pids_.push_back(0); // no pid means use 0 as self pid } - if (cpus_.empty()) { - // new review . if perfEventParanoid_ < CPU, how should be CreateMmap work? - if (perfEventParanoid_ <= PerfEventParanoid::KERNEL_USER_CPU) { - // PERF_EVENT_IOC_SET_OUTPUT doesn't support using -1 as all cpu - PutAllCpus(); - } else { - cpus_.push_back(-1); // no cpu as all cpu - } - } + } + + if (cpus_.empty()) { + PutAllCpus(); } // print info tell user which cpu and process we will select. @@ -1138,6 +1046,9 @@ bool PerfEvents::CreateMmap(const FdItem &item, const perf_event_attr &attr) void *rbuf = mmap(nullptr, (1 + mmapPages_) * pageSize_, PROT_READ | PROT_WRITE, MAP_SHARED, item.fd.Get(), 0); if (rbuf == MMAP_FAILED) { + char errInfo[ERRINFOLEN] = {0}; + strerror_r(errno, errInfo, ERRINFOLEN); + perror("errno:%d, errstr:%s", errno, errInfo); perror("Fail to call mmap \n"); return false; } diff --git a/src/subcommand_record.cpp b/src/subcommand_record.cpp index e92cffd..7023b16 100644 --- a/src/subcommand_record.cpp +++ b/src/subcommand_record.cpp @@ -23,6 +23,9 @@ #include #include #include +#if defined(CONFIG_HAS_SYSPARA) +#include +#endif #include #include #include @@ -47,6 +50,10 @@ const std::string CONTROL_CMD_STOP = "stop"; const std::string CONTROL_FIFO_FILE_C2S = "/data/local/tmp/.hiperf_record_control_c2s"; const std::string CONTROL_FIFO_FILE_S2C = "/data/local/tmp/.hiperf_record_control_s2c"; +const std::string PERF_CPU_TIME_MAX_PERCENT = "/proc/sys/kernel/perf_cpu_time_max_percent"; +const std::string PERF_EVENT_MAX_SAMPLE_RATE = "/proc/sys/kernel/perf_event_max_sample_rate"; +const std::string PERF_EVENT_MLOCK_KB = "/proc/sys/kernel/perf_event_mlock_kb"; + // when there are many events, start record will take more time. const std::chrono::milliseconds CONTROL_WAITREPY_TOMEOUT = 2000ms; @@ -619,39 +626,66 @@ bool SubCommandRecord::ParseControlCmd(const std::string cmd) return false; } +bool SubCommandRecord::SetPerfLimit(const std::string& file, const std::string& param, int value) +{ + int originValue = 0; + if (!ReadIntFromProcFile(file, originValue)) { + printf("read %s fail.", file.c_str()); + return false; + } + + if (originValue == value) { + return true; + } + + if (IsRoot()) { + return WriteIntToProcFile(file, value); + } + + if (!OHOS::system::SetParameter(param, std::to_string(value))) { + printf("set parameter %s fail.", param.c_str()); + return false; + } + isNeedSetPerfHarden_ = true; + return true; +} + bool SubCommandRecord::SetPerfCpuMaxPercent() { - int percent = 0; - if (ReadIntFromProcFile("/proc/sys/kernel/perf_cpu_time_max_percent", percent)) { - if (percent == cpuPercent_) { - return true; - } - if (!IsRoot()) { - printf("root privillege is needed to change perf_cpu_time_max_percent\n"); - return false; - } - return WriteIntToProcFile("/proc/sys/kernel/perf_cpu_time_max_percent", cpuPercent_); - } - return false; + return SetPerfLimit(PERF_CPU_TIME_MAX_PERCENT, "hiviewdfx.hiperf.perf_cpu_time_max_percent", cpuPercent_); } bool SubCommandRecord::SetPerfMaxSampleRate() { - int rate = 0; - if (ReadIntFromProcFile("/proc/sys/kernel/perf_event_max_sample_rate", rate)) { - int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY; - if (rate >= frequency) { - return true; - } - return WriteIntToProcFile("/proc/sys/kernel/perf_event_max_sample_rate", frequency); - } else { - if (!IsRoot()) { - printf("root privillege is needed to change perf_event_max_sample_rate\n"); - } else { - printf("please check if CONFIG_PERF_EVENTS enabed.\n"); + int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY; + return SetPerfLimit(PERF_EVENT_MAX_SAMPLE_RATE, "hiviewdfx.hiperf.perf_event_max_sample_rate", frequency); +} + +bool SubCommandRecord::SetPerfEventMlock() +{ + int mlock_kb = GetProcessorNum() * (mmapPages_ + 1) * 4; + return SetPerfLimit(PERF_EVENT_MLOCK_KB, "hiviewdfx.hiperf.perf_event_mlock_kb", mlock_kb); +} + +bool SubCommandRecord::SetPerfHarden() +{ + if (!isNeedSetPerfHarden_) { + return true; + } + + std::string perfHarden = OHOS::system::GetParameter(PERF_DISABLE_PARAM, "1"); + if (perfHarden == "1") { + if (!OHOS::system::SetParameter("security.perf_harden", "0")) { + printf("set parameter security.perf_harden to 0 fail."); + return false; } } - return false; + + if (!OHOS::system::SetParameter("security.perf_harden", "1")) { + printf("set parameter security.perf_harden to 1 fail."); + return false; + } + return true; } bool SubCommandRecord::TraceOffCpu() @@ -749,6 +783,17 @@ bool SubCommandRecord::PrepareSysKernel() HLOGE("Fail to set perf event cpu limit to %d\n", cpuPercent_); return false; } + + if (!SetPerfEventMlock()) { + HLOGE("Fail to set perf event mlock limit\n"); + return false; + } + + if (!SetPerfHarden()) { + HLOGE("Fail to set perf event harden\n"); + return false; + } + if (offCPU_ && !TraceOffCpu()) { HLOGE("Fail to TraceOffCpu"); return false;