hiperf支持user模式

Signed-off-by: 张建 <zhangjian22@huawei.com>
#I68M3K
This commit is contained in:
zhangjian22 2023-01-29 15:05:21 +08:00
parent a436cce44b
commit f054a14f05
8 changed files with 173 additions and 148 deletions

View File

@ -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" ]

18
etc/hiperf.cfg Normal file
View File

@ -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"
]
}]
}

15
etc/hiperf.para Normal file
View File

@ -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

15
etc/hiperf.para.dac Normal file
View File

@ -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

View File

@ -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<pid_t> pids_;
std::vector<pid_t> cpus_;

View File

@ -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<PerfEventRecord>);
bool SaveRecord(std::unique_ptr<PerfEventRecord>);
@ -271,8 +274,11 @@ private:
bool CollectionSymbol(std::unique_ptr<PerfEventRecord> 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<std::string> &callStackType);

View File

@ -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<int>::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<PerfEventParanoid>(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<pid_t> cpus)
{
cpus_ = cpus;
if (!cpus_.empty()) {
if (requestPermission_ > PerfEventParanoid::KERNEL_USER_CPU) {
requestPermission_ = PerfEventParanoid::KERNEL_USER_CPU;
}
}
}
void PerfEvents::SetPid(std::vector<pid_t> 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;
}

View File

@ -23,6 +23,9 @@
#include <ctime>
#include <memory>
#include <poll.h>
#if defined(CONFIG_HAS_SYSPARA)
#include <parameters.h>
#endif
#include <sys/stat.h>
#include <sys/utsname.h>
#include <unistd.h>
@ -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;