add hiperf

Signed-off-by: chuxuezhe11 <hanjixiao@huawei.com>
This commit is contained in:
chuxuezhe11 2021-12-24 00:46:38 -08:00
parent cb616052f3
commit eefcd9d075
296 changed files with 58233 additions and 75 deletions

10
.gitignore vendored Executable file
View File

@ -0,0 +1,10 @@
# some test file
workspace.code-workspace
.vscode
.tags
**/local.properties
**/.idea
**/.gradle
*.log
*.patch
*.pyc

476
BUILD.gn Normal file
View File

@ -0,0 +1,476 @@
# Copyright (C) 2021 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.
import("//build/ohos.gni")
import("//developtools/hiperf/hiperf.gni")
function_disable_define = [
]
config("hiperf_inner_config") {
visibility = [ ":*" ]
ldflags = []
cflags = code_check_flag
defines = function_disable_define
if(hiperf_code_analyze && is_ohos) {
cflags += code_analyze_flag
cflags -= [ "-Werror" ]
}
if(is_mingw) {
# lld: error: unable to find library -latomic
# lld: error: unable to find library -ldl
# lld: error: unable to find library -lrt
ldflags += [ "-Wl,--whole-archive", "-lpthread", "-Wl,--no-whole-archive"]
} else if(is_linux) {
ldflags += [ "-Wl,--whole-archive", "-lpthread", "-latomic", "-ldl", "-lrt", "-Wl,--no-whole-archive"]
}
include_dirs = [
"${hiperf_path}/include",
]
# debug link
# ldflags += [ "-v"]
if(hiperf_debug) {
defines += [
"HIPERF_DEBUG",
"HIPERF_DEBUG_PRINTF", # if u want to see printf in the log ?
]
} else {
# some value just for debug, but also can be opt by clang ,don't as error
# but maybe we need do some opt
cflags += [ "-Wno-unused-variable" ]
}
if(hiperf_check_time) {
defines += [ "HIPERF_DEBUG_TIME", ]
}
cflags += [ "-std=c++17" ]
if(is_linux) {
cflags += [ "-g" ] # we need debug info when it crash.
}
defines += [ "is_mingw=${is_mingw}" ]
defines += [ "is_linux=${is_linux}" ]
defines += [ "is_ohos=${is_ohos}" ]
defines += [ "is_double_framework=${is_double_framework}" ]
if(hiperf_target_host) {
defines += [ "target_cpu_${host_cpu}" ]
} else {
defines += [ "target_cpu_${target_cpu}" ]
}
if(is_mingw) {
cflags += [ "-includeMingW64Fix.h" ]
cflags += [ "-Wno-inconsistent-dllimport" ] # in mingw some sec api will overwrite by utilsecurec
defines +=[ "WIN32_LEAN_AND_MEAN" ]
include_dirs += [
"${hiperf_path}/include/nonlinux/",
"//third_party/Linux_Kernel/include/uapi"
]
}
if(hiperf_test_coverage && is_ohos) {
cflags += [ "-fprofile-arcs", "-ftest-coverage" ]
ldflags += [ "--coverage" ]
}
}
sources_platform_common = [
"./src/perf_file_format.cpp",
"./src/command.cpp",
"./src/subcommand.cpp",
"./src/option.cpp",
"./src/utilities.cpp",
"./src/symbols_file.cpp",
"./src/virtual_runtime.cpp",
"./src/virtual_thread.cpp",
"./src/perf_file_reader.cpp",
"./src/perf_event_record.cpp",
"./src/dwarf_encoding.cpp",
"./src/elf_file.cpp",
"./src/elf_header.cpp",
"./src/section_header.cpp",
"./src/program_header.cpp",
"./src/elf_symbol.cpp",
"./src/subcommand_help.cpp",
"./src/subcommand_dump.cpp",
"./src/subcommand_report.cpp",
"./src/report.cpp",
"./src/report_json_file.cpp",
"./src/register.cpp",
"./src/callstack.cpp",
]
if(hiperf_debug) {
sources_platform_common += [
"./src/debug_logger.cpp",
"./src/option_debug.cpp",
]
}
sources_platform_linux = [
"./src/perf_events.cpp",
"./src/tracked_command.cpp",
"./src/ring_buffer.cpp",
"./src/perf_file_writer.cpp",
"./src/subcommand_stat.cpp",
"./src/subcommand_record.cpp",
"./src/subcommand_list.cpp",
]
common_deps = [
"//utils/native/base:utilsecurec",
"//third_party/zlib:libz",
":support_elf",
":support_protobuf",
":support_libunwind"
]
if(!hiperf_use_libunwind) {
common_deps -= [ ":support_libunwind" ]
}
if (is_ohos) {
common_deps += [ "//utils/native/base:utilsecurec_shared" ]
common_deps -= [ "//utils/native/base:utilsecurec" ]
}
if(hiperf_target_static) {
common_deps -= [ ":support_protobuf" ]
}
common_configs = [
":hiperf_inner_config",
"//utils/native/base:utils_config",
"//third_party/googletest:gtest_config",
]
config("hiperf_syspara_config") {
defines = [ "CONFIG_HAS_SYSPARA" ]
}
if(is_ohos && hiperf_use_syspara) {
common_configs += [ ":hiperf_syspara_config" ]
common_deps += [ "//base/startup/syspara_lite/interfaces/innerkits/native/syspara:syspara" ]
}
ohos_source_set("hiperf_platform_common") {
use_exceptions = true
public_deps = common_deps
public_configs = common_configs
defines = []
if(is_ohos) {
external_deps = [
"hiviewdfx_hilog_native:libhilog",
]
} else {
defines += [ "CONFIG_NO_HILOG" ]
}
sources = sources_platform_common
}
config("platform_linux_config") {
defines = ["SUPPORT_PERF_EVENT"]
}
ohos_source_set("hiperf_platform_linux") {
use_exceptions = true
public_deps = common_deps
public_configs = common_configs
public_configs += [ ":platform_linux_config" ]
configs = [ "interfaces/innerkits/native:hiperf_client_config" ]
sources = sources_platform_linux
}
config("unwind_config") {
defines = [ "HAVE_LIBUNWIND=1" ]
}
ohos_source_set("support_libunwind") {
public_configs = common_configs
public_configs += [
":unwind_config",
]
if(hiperf_target_host) {
public_deps = [ "//third_party/libunwind:unwind_source_${host_cpu}" ]
} else {
public_deps = [ "//third_party/libunwind:unwind_source_${target_cpu}" ]
}
}
config("elf_config") {
}
ohos_source_set("support_elf") {
public_configs = common_configs
public_configs += [
":elf_config",
]
}
config("protobuf_config") {
defines = [ "HAVE_PROTOBUF=1" ]
cflags = [ "-Wno-unused-parameter" ] # proto buf head build error
include_dirs = [
"//third_party/protobuf/src",
"//third_party/protobuf/src/google",
"//third_party/protobuf/src/google/protobuf"
]
}
ohos_source_set("support_protobuf") {
use_exceptions = true
#protobuf
public_configs = common_configs
public_configs += [
":protobuf_config",
]
public_deps = [
":proto_file_cpp",
"//utils/native/base:utilsecurec"
]
sources = [ "./src/report_protobuf_file.cpp" ]
}
#protobuf {
proto_file_defines = [
# add your proto file here
"report_sample"
]
proto_base_dir = "proto"
proto_out_dir = "$target_gen_dir" + "/" + proto_base_dir
proto_file_codegen = []
proto_file_sources = []
foreach(proto_file, proto_file_defines) {
proto_file_codegen += [
"$proto_out_dir" + "/" + "$proto_file.pb.h",
"$proto_out_dir" + "/" + "$proto_file.pb.cc",
]
proto_file_sources += [
"$proto_base_dir" + "/" + "$proto_file.proto"
]
}
# this is so bad , but someone config the protoc's subsystem_name
# the better way is build system need provider host tools path or prebuild tools path
if(is_double_framework) {
protoc_subsystem_out_path = "developtools/profiler"
} else {
protoc_subsystem_out_path = "developtools/developtools"
}
if(default_toolchain == current_toolchain) {
#if target build
host_out_path = "/" + get_label_info(host_toolchain,"name")
} else {
#if host build (for some linke mingw)
host_out_path = "/../" + get_label_info(host_toolchain,"name")
}
host_protoc_path = root_out_dir + host_out_path + "/" + protoc_subsystem_out_path + "/protoc"
action("hiperf_host_build_proto") {
deps = [ "//third_party/protobuf:protoc(//build/toolchain/linux:clang_x64)" ]
args = []
outputs = proto_file_codegen
sources = []
script = "proto/build_proto.sh"
args += [ rebase_path(host_protoc_path) ]
args += [ "--proto_path", rebase_path(proto_base_dir) ]
args += [ "--cpp_out",rebase_path(proto_out_dir)]
foreach(proto_file_source, proto_file_sources) {
#tell gn to check which files as source time
sources += [ rebase_path(proto_file_source) ]
args += [ rebase_path(proto_file_source) ]
}
}
config("proto_file_cpp_config") {
include_dirs = [
proto_out_dir
]
}
ohos_source_set("proto_file_cpp") {
cflags = []
deps = [ ":hiperf_host_build_proto"]
public_deps = [ "//third_party/protobuf:protobuf_lite_static" ]
sources = proto_file_codegen
public_configs = [ ":proto_file_cpp_config" ]
}
#protobuf }
ohos_executable("hiperf") {
install_enable = true
sources = [ "./src/main.cpp" ]
deps = [
":hiperf_platform_common",
":hiperf_platform_linux",
]
if(hiperf_target_static) {
static_link = true
}
if(is_linux || is_mingw) {
# ld.lld: error: attempted static link of dynamic object hiviewdfx/hilog_native/libhilog.so
static_link = true
}
subsystem_name = "developtools"
part_name = "hiperf"
}
ohos_executable("hiperf_host") {
sources = [ "./src/main.cpp" ]
deps = [ ":hiperf_platform_common" ]
static_link = true
subsystem_name = "developtools"
part_name = "hiperf"
}
ohos_source_set("hiperf_platform_host") {
sources = [ "./src/hiperf_libreport.cpp" ]
public_deps = [ ":hiperf_platform_common" ]
}
ohos_shared_library("hiperf_host_lib") {
public_deps = [ ":hiperf_platform_host" ]
output_name = "hiperf_report"
ldflags = [ "-static-libstdc++" ]
subsystem_name = "developtools"
part_name = "hiperf"
}
ohos_executable("hiperf_host_lib_demo") {
sources = [ "./src/hiperf_libreport_demo.cpp" ]
deps = [ ":hiperf_host_lib" ]
include_dirs = [ "${hiperf_path}/include" ]
subsystem_name = "developtools"
part_name = "hiperf"
}
ohos_copy("hiperf_host_python") {
sources = [ "./script" ]
outputs = [ target_out_dir + "/host/" ]
module_source_dir = target_out_dir + "/$target_name"
module_install_name = ""
subsystem_name = "developtools"
part_name = "hiperf"
}
ohos_source_set("hiperf_code_analyze") {
deps = [
":hiperf_platform_common",
":hiperf_platform_linux",
]
}
group("hiperf_target") {
if(hiperf_target_host) {
deps = [ ":hiperf(${host_toolchain})" ]
} else {
deps = [ ":hiperf"]
}
}
group("hiperf_test_target") {
testonly = true
deps = [ "test:hiperf_test" ]
}
group("hiperf_target_all") {
if(is_double_framework) {
deps = [
":hiperf_target"
]
} else {
deps = [
":hiperf_target",
":hiperf_host(//build/toolchain/linux:clang_x64)", # host linux
":hiperf_host(//build/toolchain/mingw:mingw_x86_64)", # host mingw
":hiperf_host_lib_demo(//build/toolchain/linux:clang_x64)", # host linux
":hiperf_host_lib(//build/toolchain/linux:clang_x64)", # host linux
":hiperf_host_lib(//build/toolchain/mingw:mingw_x86_64)", # host mingw
":hiperf_host_python",
"interfaces/innerkits/native:hiperf_client", # c++ api
]
if(is_ohos) {
deps += [ "interfaces/kits/js/napi:hiperf_client_napi", ] # js api
}
}
}
group("hiperf_demo") {
if(hiperf_target_host) {
deps = [
"demo/cpp:hiperf_demo(${host_toolchain})",
]
} else {
deps = [
"demo/cpp:hiperf_demo",
]
}
}
group("hiperf_example_cmd") {
if(hiperf_target_host) {
deps = [
"demo/cpp:hiperf_example_cmd(${host_toolchain})",
]
} else {
deps = [
"demo/cpp:hiperf_example_cmd",
]
}
}
group("hiperf_all") {
testonly = true
if (hiperf_code_analyze) {
deps = [
":hiperf_code_analyze"
]
} else {
deps = [
":hiperf_target_all",
":hiperf_example_cmd",
]
if(!is_double_framework) {
deps += [
":hiperf_test_target",
":hiperf_demo"
]
}
}
}

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

109
OAT.xml Executable file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2021 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.
Notes:
This is project config file for OpenHarmony OSS Audit Tool, if you have any questions or concerns, please email chenyaxun@huawei.com.
-->
<!-- OAT(OSS Audit Tool) configuration guide:
basedir: Root dir, the basedir + project path is the real source file location.
licensefile:
1.If the project don't have "LICENSE" in root dir, please define all the license files in this project in , OAT will check license files according to this rule.
tasklist(only for batch mode):
1. task: Define oat check thread, each task will start a new thread.
2. task name: Only an name, no practical effect.
3. task policy: Default policy for projects under this task, this field is required and the specified policy must defined in policylist.
4. task filter: Default filefilter for projects under this task, this field is required and the specified filefilter must defined in filefilterlist.
5. task project: Projects to be checked, the path field define the source root dir of the project.
policyList:
1. policy: All policyitems will be merged to default OAT.xml rules, the name of policy doesn't affect OAT check process.
2. policyitem: The fields type, name, path, desc is required, and the fields rule, group, filefilter is optional,the default value is:
<policyitem type="" name="" path="" desc="" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
3. policyitem type:
"compatibility" is used to check license compatibility in the specified path;
"license" is used to check source license header in the specified path;
"copyright" is used to check source copyright header in the specified path;
"import" is used to check source dependency in the specified path, such as import ... ,include ...
"filetype" is used to check file type in the specified path, supported file types: archive, binary
"filename" is used to check whether the specified file exists in the specified path(support projectroot in default OAT.xml), supported file names: LICENSE, README, README.OpenSource
4. policyitem name: This field is used for define the license, copyright, "*" means match all, the "!" prefix means could not match this value. For example, "!GPL" means can not use GPL license.
5. policyitem path: This field is used for define the source file scope to apply this policyitem, the "!" prefix means exclude the files. For example, "!.*/lib/.*" means files in lib dir will be exclude while process this policyitem.
6. policyitem rule and group: These two fields are used together to merge policy results. "may" policyitems in the same group means any one in this group passed, the result will be passed.
7. policyitem filefilter: Used to bind filefilter which define filter rules.
8. filefilter: Filter rules, the type filename is used to filter file name, the type filepath is used to filter file path.
Note:If the text contains special characters, please escape them according to the following rules:
" == &gt;
& == &gt;
' == &gt;
< == &gt;
> == &gt;
-->
<configuration>
<oatconfig>
<filefilterlist>
<filefilter name="defaultPolicyFilter" desc="Filters for compatibilitylicense header policies">
<filteritem type="filepath" name="test/unittest/resource/testdata/.*" desc="test resource file, no license header"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/.*" desc="test resource file, no license header"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/report/.*" desc="test resource file, no license header"/>
</filefilter>
<filefilter name="copyrightPolicyFilter" desc="Filters for copyright header policies" >
<filteritem type="filepath" name="test/unittest/resource/testdata/.*" desc="test resource file, no copyright header"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/.*" desc="test resource file, no copyright header"/>
</filefilter>
<filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies">
<filteritem type="filepath" name="test/unittest/resource/test/unittest/resource/testdata/ehdr_from_readelf_64" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf_test" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/perf.data" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf_test_stripped_nobuildid" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf32_test_stripped_noehframehdr" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/perf2.data" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dummpfile" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/ehdr_from_readelf_32" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/phdrs_from_readelf_64" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf32_test" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf_test_stripped_noehframehdr" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/shdrs_from_readelf_64" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/vmlinux_stripped" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/proto_test" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf32_test_stripped" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/syms_from_readelf_64" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf32_test_broken" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf32_test_stripped_nobuildid" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/perf.data" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/hiperf_example_cmd" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/user_data.dump" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/hiperf_643_644_sample_record_213_6754913387560.dump" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/dwarf/user_regs.dump" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/empytfile" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/phdrs_from_readelf_32" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/vmlinux_stripped_broken" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/vmlinux_stripped_nobuildid" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/shdrs_from_readelf_32" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf_test_stripped" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/elf_test_stripped_broken" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/vmlinux" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/syms_from_readelf_32" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/report/.*" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/symbols_file_test_elf32" desc="the binary file for test, the file is self-developed"/>
<filteritem type="filepath" name="test/unittest/resource/testdata/symbols_file_test_elf64" desc="the binary file for test, the file is self-developed"/>
</filefilter>
</filefilterlist>
</oatconfig>
</configuration>

View File

@ -1,36 +0,0 @@
# developtools_hiperf
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@ -1,39 +0,0 @@
# developtools_hiperf
#### 介绍
{**以下是 Gitee 平台说明,您可以替换此简介**
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN。专为开发者提供稳定、高效、安全的云端软件开发协作平台
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
#### 软件架构
软件架构说明
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
#### 使用说明
1. xxxx
2. xxxx
3. xxxx
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

407
README_zh.md Executable file

File diff suppressed because one or more lines are too long

29
demo/cpp/BUILD.gn Normal file
View File

@ -0,0 +1,29 @@
# Copyright (C) 2021 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.
import("//build/ohos.gni")
import("//developtools/hiperf/hiperf.gni")
ohos_executable("hiperf_demo") {
deps = [ "${hiperf_path}/interfaces/innerkits/native:hiperf_client" ]
sources = [ "hiperf_demo.cpp" ]
subsystem_name = "developtools"
part_name = "hiperf"
}
ohos_executable("hiperf_example_cmd") {
cflags = [ "-O0" ]
sources = [ "hiperf_example_cmd.cpp" ]
subsystem_name = "developtools"
part_name = "hiperf"
}

93
demo/cpp/hiperf_demo.cpp Normal file
View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2021 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.
*/
#include <thread>
#include <unistd.h>
#include <vector>
#include "hiperf_client.h"
using namespace OHOS::Developtools::HiPerf;
namespace HiperfClientDemo {
void TestCodeThread(int id)
{
constexpr uint32_t k = 1024;
constexpr uint32_t k2 = 2 * k;
constexpr uint32_t k10 = 10 * k;
constexpr uint32_t two = 2;
constexpr uint64_t thousand = 1000;
std::vector<std::unique_ptr<char[]>> mems;
printf("TestCodeThread %d:++\n", id);
std::this_thread::sleep_for(std::chrono::milliseconds(thousand));
for (uint32_t i = 0; i < k10; i++) {
if (i % two == 0) {
mems.push_back(std::make_unique<char[]>(k));
} else {
mems.push_back(std::make_unique<char[]>(k2));
}
}
for (uint32_t i = 0; i < k10; i++) {
mems.pop_back();
}
std::this_thread::sleep_for(std::chrono::milliseconds(thousand));
printf("TestCodeThread %d:--\n", id);
}
} // namespace HiperfClientDemo
using namespace HiperfClientDemo;
int main()
{
const int waitTime = 2;
const int countTry = 3;
HiperfClient::Client myHiperf;
printf("GetOutputDir:'%s'\n", myHiperf.GetOutputDir().c_str());
printf("GetCommandPath:'%s'\n", myHiperf.GetCommandPath().c_str());
myHiperf.SetDebugMode();
printf("demo start\n");
HiperfClient::RecordOption opt;
const int timeout = 30;
opt.SetAppPackage("com.ohos.launcher");
opt.SetTimeStopSec(timeout);
if (myHiperf.Start(opt)) {
printf("demo start successfully\n");
}
std::thread workload(TestCodeThread, 0);
sleep(waitTime);
// try for each thread times
for (int i = 0; i < countTry; i++) {
printf("demo pause\n");
if (!myHiperf.Pause()) {
printf("demo pause failed\n");
}
sleep(1);
printf("demo resume \n");
if (!myHiperf.Resume()) {
printf("demo resume failed\n");
}
sleep(1);
}
printf("demo stop \n");
if (myHiperf.Stop()) {
printf("demo stop successfully\n");
}
workload.join();
return 0;
};

483
demo/cpp/hiperf_example_cmd.cpp Executable file
View File

@ -0,0 +1,483 @@
/*
* Copyright (c) 2021 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.
*/
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstring>
#include <iostream>
#include <random>
#include <string>
#include <thread>
#include <vector>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
using namespace std::chrono;
using namespace std::chrono_literals;
namespace {
#define USED_FUNCTION __attribute__((__used__)) __attribute__((optnone))
constexpr milliseconds eachStackFunRunTime = 100ms;
constexpr milliseconds msDuartion = 1000ms;
extern USED_FUNCTION void LoopBranch0(std::default_random_engine &rnd, int level);
extern USED_FUNCTION void LoopBranch1(std::default_random_engine &rnd, int level);
struct Option {
int numThreads {5};
int second {36000};
int stack {5};
bool noWait = false;
bool dynamicStack = false;
bool mmap = false;
bool iowait = false;
bool branch = false;
bool nonew = false;
bool nofunc = false;
int boundCpu {-1};
int sleepms {0};
};
inline int GetTid()
{
int res = static_cast<int>(syscall(SYS_gettid));
return res;
}
USED_FUNCTION void LoopDummy(milliseconds anything)
{
if (anything.count() > 0) {
printf("");
}
}
USED_FUNCTION void LoopBranch0(std::default_random_engine &rnd, int level)
{
constexpr int two {2};
if (level == 0) {
printf("");
return;
}
if (rnd() % two == 0) {
LoopBranch0(rnd, --level);
} else {
LoopBranch1(rnd, --level);
}
}
USED_FUNCTION void LoopBranch1(std::default_random_engine &rnd, int level)
{
constexpr int two {2};
if (level == 0) {
printf("");
return;
}
if (rnd() % two == 0) {
LoopBranch0(rnd, --level);
} else {
LoopBranch1(rnd, --level);
}
}
USED_FUNCTION void LoopBranch()
{
constexpr int two {2};
int branchLevel = 10;
std::default_random_engine rnd;
if (rnd() % two == 0) {
LoopBranch0(rnd, branchLevel);
} else {
LoopBranch1(rnd, branchLevel);
}
}
USED_FUNCTION void LoopIowait()
{
std::default_random_engine rnd;
std::unique_ptr<FILE, decltype(&fclose)> fd {fopen("temp", "rw"), &fclose};
if (fd != nullptr) {
const std::string tempBuf = std::to_string(rnd());
fwrite(tempBuf.c_str(), tempBuf.size(), 1, fd.get());
}
}
USED_FUNCTION void LoopMmap()
{
int *arr = static_cast<int *>(
mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, 0, 0));
int *ptr = arr;
int someVal1 {10};
int someVal2 {20};
int someVal3 {30};
*ptr = someVal1;
++ptr;
*ptr = someVal2;
++ptr;
*ptr = someVal3;
munmap(arr, getpagesize());
return;
}
USED_FUNCTION void LoopFunction(milliseconds timeOutMS, const Option &option)
{
auto now = std::chrono::steady_clock::now();
auto sleepTime = now + seconds(1);
int count = 0;
int a {};
int b {};
int c {};
while (std::chrono::steady_clock::now() < (now + timeOutMS)) {
if (option.sleepms > 0) {
if (std::chrono::steady_clock::now() >= sleepTime) {
sleepTime = std::chrono::steady_clock::now() + seconds(1);
std::this_thread::sleep_for(std::chrono::milliseconds(option.sleepms));
}
}
if (option.mmap) {
LoopMmap();
}
if (option.iowait) {
LoopIowait();
}
if (option.branch) {
LoopBranch();
}
a = b++ * c++;
b = a++ * c++;
c = b++ * a++;
if (!option.nonew) {
auto p = new unsigned int;
*p = count++;
delete p;
p = nullptr;
}
if (!option.nofunc) {
LoopDummy(timeOutMS);
}
}
return;
}
inline void Loop(milliseconds timeOutMS, const Option &option)
{
printf("loop at %s\n", __func__);
LoopFunction(timeOutMS, option);
return;
}
USED_FUNCTION void CallStack10(int currentStack, const Option &option)
{
if (option.stack > 0) {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack9(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack10(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack8(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack9(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack7(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack8(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack6(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack7(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack5(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack6(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack4(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack5(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack3(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack4(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack2(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack3(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack1(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack2(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void CallStack0(int currentStack, const Option &option)
{
if (option.stack > 0) {
if (option.dynamicStack) {
Loop(eachStackFunRunTime, option); // loop 100 ms
}
CallStack1(currentStack - 1, option);
} else {
Loop(option.second * msDuartion, option);
}
}
USED_FUNCTION void ExampleThread(const Option &option)
{
printf("thread %d ++ with %d %d \n", GetTid(), option.second, option.stack);
CallStack0(option.stack, option);
printf("thread %d --\n", GetTid());
return;
}
USED_FUNCTION void RunSampleThread(const Option &option)
{
printf("run %d threads for %d second with %d stack level\n", option.numThreads, option.second,
option.stack);
printf("main thread %d\n", GetTid());
std::thread threads[option.numThreads];
for (int count = 0; count < option.numThreads; ++count) {
threads[count] = std::thread(ExampleThread, option);
}
for (int count = 0; count < option.numThreads; ++count) {
threads[count].join();
}
printf("all thread exit\n");
}
void WaitStart()
{
std::string startArg;
std::cout << "Please input 'start' to begin the subthread: \n";
while (true) {
std::cin >> startArg;
if (startArg.compare("start") == 0) {
break;
} else {
break;
}
}
}
void Help()
{
printf("this is a demo test comand\n");
printf(" Use the following commands to simulate different scenarios\n");
printf(" --help\n");
printf(" this page\n");
printf(" --thread <number>\n");
printf(" setup the thread number, default is 5 second\n");
printf(" --time <time>\n");
printf(" setup run sec, default is 36000 second\n");
printf(" --stack <level>\n");
printf(" setup stack level, default is 5\n");
printf(" --nowait\n");
printf(" setup skip the start, default wait the start\n");
printf(" --dynamic\n");
printf(" will run some code in each stack level\n");
printf(" --mmap\n");
printf(" will run mmap code in the loop\n");
printf(" --iowait\n");
printf(" will run iowait code in the loop\n");
printf(" --branch\n");
printf(" will run branch code in the loop\n");
printf(" --nonew\n");
printf(" will not new memory in the loop\n");
printf(" --nofunc\n");
printf(" will not call dummy func in the loop\n");
printf(" --boundcpu <cpu>\n");
printf(" the process will bound to <cpu>\n");
printf(" --sleep <milliseconds>\n");
printf(" threads will sleep <milliseconds> per second, defaut is 0.\n");
}
bool GetIntFromArg(std::vector<std::string> &args, int &value)
{
if (!args.empty()) {
if (std::all_of(args.begin()->begin(), args.begin()->end(), ::isdigit)) {
value = std::stoi(args[0]);
args.erase(args.begin());
} else {
printf("unknow format '%s'\n", args[0].c_str());
return false;
}
}
return true;
}
bool MatchArgs(std::vector<std::string> &args, const std::string &option)
{
if (args[0] == option) {
args.erase(args.begin());
return true;
}
return false;
}
bool GetBoolFromArg(std::vector<std::string> &args, const std::string &option, bool &value)
{
if (MatchArgs(args, option)) {
value = true;
return true;
} else {
return false;
}
}
} // namespace
USED_FUNCTION int main(int argc, char *argv[])
{
std::vector<std::string> args;
for (int i = 1; i < argc; i++) {
args.push_back(argv[i]);
}
Option option;
while (!args.empty()) {
if (MatchArgs(args, "--help")) {
Help();
return 0;
} else if (MatchArgs(args, "--boundcpu")) {
if (!GetIntFromArg(args, option.boundCpu)) {
return -1;
}
} else if (MatchArgs(args, "--sleep")) {
if (!GetIntFromArg(args, option.sleepms)) {
return -1;
}
} else if (MatchArgs(args, "--thread")) {
if (!GetIntFromArg(args, option.numThreads)) {
return -1;
}
} else if (MatchArgs(args, "--time")) {
if (!GetIntFromArg(args, option.second)) {
return -1;
}
} else if (MatchArgs(args, "--stack")) {
if (!GetIntFromArg(args, option.stack)) {
return -1;
}
} else if (GetBoolFromArg(args, "--dynamic", option.dynamicStack)) {
continue;
} else if (GetBoolFromArg(args, "--nowait", option.noWait)) {
continue;
} else if (GetBoolFromArg(args, "--mmap", option.mmap)) {
continue;
} else if (GetBoolFromArg(args, "--iowait", option.iowait)) {
continue;
} else if (GetBoolFromArg(args, "--branch", option.branch)) {
continue;
} else if (GetBoolFromArg(args, "--nonew", option.nonew)) {
continue;
} else if (GetBoolFromArg(args, "--nofunc", option.nofunc)) {
continue;
} else {
printf("unknow format '%s'\n", args[0].c_str());
return -1;
}
}
if (option.boundCpu > -1) {
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(option.boundCpu, &mask);
if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
printf("Set CPU(%d) affinity failue, ERROR:%s\n", option.boundCpu, strerror(errno));
}
}
if (!option.noWait) {
WaitStart();
}
RunSampleThread(option);
return 0;
}

41
demo/cpp/hiperf_malloc_demo.cpp Executable file
View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021 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.
*/
#include <thread>
#include <unistd.h>
#include <vector>
#include "hiperf_client.h"
using namespace OHOS::Developtools::HiPerf;
using namespace HiperfMallocDemo;
int main(const int argc, const char *argv[])
{
static const int wartTime = 100;
static const int countTry = 1000;
const int bufSize = 1048576;
printf("demo start\n");
sleep(wartTime);
void *temp = nullptr;
// try for each thread times
for (int i = 0; i < countTry; i++) {
temp = malloc(bufSize);
printf("malloc %d \n", i);
sleep(wartTime);
}
printf("demo stop \n");
return 0;
};

16
demo/js/.gitignore vendored Executable file
View File

@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
/entry/.preview
.cxx
/node_modules

59
demo/js/build.gradle Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2021 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.
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: 'com.huawei.ohos.app'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
signingConfigs {
debug {
storeFile file('signature\\demo.p12')
storePassword '000000199FC9A84CDB5260157B200958D21EDB95A83C2D4D71B4B4696D30BF1E7062E591FD073A04B4'
keyAlias = 'demo'
keyPassword '0000001921F600EE5BC1084649F3B296949A7E9153261C47FADB854CFC10454965D7AC8486D42CBD66'
signAlg = 'SHA256withECDSA'
profile file('signature\\demo.p7b')
certpath file('signature\\demo.cer')
}
}
compileSdkVersion 7
supportSystem "standard"
}
buildscript {
repositories {
maven {
url 'https://repo.huaweicloud.com/repository/maven/'
}
maven {
url 'https://developer.huawei.com/repo/'
}
}
dependencies {
classpath 'com.huawei.ohos:hap:3.0.3.4'
classpath 'com.huawei.ohos:decctest:1.2.6.0'
}
}
allprojects {
repositories {
maven {
url 'https://repo.huaweicloud.com/repository/maven/'
}
maven {
url 'https://developer.huawei.com/repo/'
}
}
}

2
demo/js/entry/.gitignore vendored Executable file
View File

@ -0,0 +1,2 @@
/build
/node_modules

View File

@ -0,0 +1,21 @@
apply plugin: 'com.huawei.ohos.hap'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
compileSdkVersion 7
defaultConfig {
compatibleSdkVersion 7
}
buildTypes {
release {
proguardOpt {
proguardEnabled false
rulesFiles 'proguard-rules.pro'
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
testImplementation 'junit:junit:4.13.1'
}

1
demo/js/entry/package.json Executable file
View File

@ -0,0 +1 @@
{}

1
demo/js/entry/proguard-rules.pro vendored Executable file
View File

@ -0,0 +1 @@
# config module specific ProGuard rules here.

View File

@ -0,0 +1,63 @@
{
"app": {
"bundleName": "com.example.hiperfdemo",
"vendor": "example",
"version": {
"code": 1000000,
"name": "1.0.0"
}
},
"deviceConfig": {},
"module": {
"package": "com.example.hiperfdemo",
"name": ".MyApplication",
"mainAbility": ".MainAbility",
"deviceType": [
"phone"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry",
"installationFree": false
},
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"orientation": "unspecified",
"visible": true,
"srcPath": "MainAbility",
"name": ".MainAbility",
"srcLanguage": "js",
"icon": "$media:icon",
"description": "$string:description_mainability",
"formsEnabled": false,
"label": "$string:entry_MainAbility",
"type": "page",
"launchType": "standard"
}
],
"js": [
{
"pages": [
"pages/index/index",
"pages/second/second"
],
"name": ".MainAbility",
"window": {
"designWidth": 720,
"autoDesignWidth": false
}
}
]
}
}

View File

@ -0,0 +1,8 @@
export default {
onCreate() {
console.info("Application onCreate");
},
onDestroy() {
console.info("Application onDestroy");
}
};

View File

@ -0,0 +1,15 @@
{
"strings": {
"hello": "Hello",
"world": "World",
"page": "Second Page",
"next": "Next Page",
"back": "Back",
"test_record": "Test record",
"test_option": "Test option",
"test_click": "Test click",
"output": "Output"
},
"Files": {
}
}

View File

@ -0,0 +1,15 @@
{
"strings": {
"hello": "您好",
"world": "世界",
"page": "第二页",
"next": "下一页",
"back": "返回",
"test_record": "测试 record",
"test_option": "测试 option",
"test_click": "测试 点击",
"output": "测试输出"
},
"Files": {
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 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.
*/
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
.title {
font-size: 60px;
text-align: center;
width: 100%;
height: 40%;
margin: 10px;
}
.btn {
margin: 2%;
width: 50%;
height: 100px;
font-size: 40px;
}

View File

@ -0,0 +1,21 @@
<!--
Copyright (c) 2021 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.
-->
<div class="container">
<text>{{output}}</text>
<input class="btn" type="button" value="{{ $t('strings.test_record') }}" onclick="recordOnclick"></input>
<input class="btn" type="button" value="{{ $t('strings.test_option') }}" onclick="optionOnclick"></input>
<input class="btn" type="button" value="{{ $t('strings.test_click') }}" onclick="testOnclick"></input>
</div>

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2021 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.
*/
import hiperf from '@ohos.hiperf'
export default {
data: {
clickTimes: 0,
total: 20,
output: '',
},
onInit() {
this.output = this.clickTimes;
},
sleep(delay) {
let start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
},
optionOnclick: function () {
this.clickTimes++;
console.info("optionOnclick" + this.clickTimes);
var result
this.output = ""
result = hiperf.resetOption();
this.output += ('ResetOption: ' + result + '\n');
result = hiperf.setOutputFilename("/data/accounts/account_0/appdata/com.example.hiperfdemo/files/perf.data");
this.output += ('SetOutputFilename: ' + result+ '\n');
result = hiperf.getOutputFileName();
this.output += ('GetOutputFileName: ' + result+ '\n');
result = hiperf.setTargetSystemWide(true);
result = hiperf.setCompressData(true);
result = hiperf.setSelectCpus("1,2,3,4");
result = hiperf.setTimeStopSec(2);
result = hiperf.setFrequency(500);
result = hiperf.setPeriod(100);
result = hiperf.setSelectEvents("hw-cpu-cycles,hw-instructions");
result = hiperf.setSelectGroups("hw-cpu-cycles,hw-instructions");
result = hiperf.setNoInherit(true);
result = hiperf.setSelectPids("1,2,3");
result = hiperf.setSelectTids("1,2,3");
result = hiperf.setExcludePerf(true);
result = hiperf.setCpuPercent(25);
result = hiperf.setOffCPU(false);
result = hiperf.setCallGraph("dwarf");
result = hiperf.setDelayUnwind(true);
result = hiperf.setDisableUnwind(true);
result = hiperf.setDisableCallstackMerge(true);
result = hiperf.setSymbolDir("/data/local/tmp");
result = hiperf.setDataLimit("10M");
result = hiperf.setAppPackage("com.example.hiperfdemo");
result = hiperf.setClockId("monotonic");
result = hiperf.setVecBranchSampleTypes("any_call");
result = hiperf.setMmapPages(1024);
result = hiperf.getOptionVecString();
this.output += ('GetOptionVecString: ' + result+ '\n');
result = hiperf.startWithOption();
this.output += ('StartWithOption: ' + result+ '\n');
},
recordOnclick: function () {
this.clickTimes++;
console.info("onclick recordOnclick" + this.clickTimes);
this.output = ("recordOnclick\n" + this.clickTimes + '\n')
var result
result = hiperf.setDebugMode();
this.output += ('SetDebugMode: ' + result + '\n');
if(!result) {
return;
}
hiperf.setup("/data/accounts/account_0/appdata/com.example.hiperfdemo/files");
this.output += ('Setup: ' + result+ '\n');
if(!result) {
return;
}
result = hiperf.isReady();
this.output += ('IsReady: ' + result+ '\n');
if(!result) {
return;
}
result = hiperf.getOutputPerfDataPath();
this.output += ('GetOutputPerfDataPath: ' + result+ '\n');
result = hiperf.getCommandPath();
this.output += ('GetCommandPath: ' + result+ '\n');
result = hiperf.start();
this.output += ('Start: ' + result+ '\n');
if(!result) {
return;
}
this.sleep(1000);
result = hiperf.pause();
this.output += ('Pause: ' + result+ '\n');
if(!result) {
return;
}
this.sleep(1000);
result = hiperf.resume();
this.output += ('Resume: ' + result+ '\n');
if(!result) {
return;
}
this.sleep(1000);
result = hiperf.stop();
this.output += ('Stop: ' + result+ '\n');
},
testOnclick: function ()
{
this.clickTimes++;
console.info("onclick testOnclick " + this.clickTimes);
this.output = this.clickTimes
}
}

View File

@ -0,0 +1,24 @@
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
.title {
font-size: 60px;
text-align: center;
width: 100%;
height: 40%;
margin: 10px;
}
.btn {
width: 50%;
height: 100px;
font-size: 40px;
}

View File

@ -0,0 +1,6 @@
<div class="container">
<text class="title">
{{ $t('strings.page') }}
</text>
<input class="btn" type="button" value="{{ $t('strings.back') }}" onclick="onclick"></input>
</div>

View File

@ -0,0 +1,12 @@
import router from '@system.router'
export default {
data: {
title: 'World'
},
onclick: function () {
router.replace({
uri: "pages/index/index"
})
}
}

View File

@ -0,0 +1,12 @@
{
"string": [
{
"name": "entry_MainAbility",
"value": "HiperfDemo"
},
{
"name": "description_mainability",
"value": "Hiperf Demo API JS"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

1
demo/js/package.json Executable file
View File

@ -0,0 +1 @@
{}

15
demo/js/settings.gradle Normal file
View File

@ -0,0 +1,15 @@
/*
* Copyright (c) 2021 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.
*/
include ':entry'

View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIICEjCCAZWgAwIBAgIEMw2S7TAMBggqhkjOPQQDAwUAMGMxCzAJBgNVBAYTAkNO
MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh
bTEjMCEGA1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwHhcNMjEwODIy
MTM0MTM3WhcNMzEwODIwMTM0MTM3WjBGMQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJ
MAcGA1UEBxMAMQkwBwYDVQQKEwAxCTAHBgNVBAsTADENMAsGA1UEAxMERGVtbzBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABKZosHNfe+WW7xq9cGZ0uqexn3+weRCB
DHAY29wJJDpNEwNccWmliQfnvSUJ+xoEb2Byg8MSd+QgrK3efKUmdvajUjBQMB0G
A1UdDgQWBBRG9VxvjDGQKMLP8s0zkPN7abWzTDAOBgNVHQ8BAf8EBAMCB4AwHwYD
VR0jBBgwFoAU24a3IhbVC6FLt90le7nxBX2iLUcwDAYIKoZIzj0EAwMFAANpADBm
AjEA0DIHno+XmlX4MFSOfFEJq58/rNGC1tUM1ZzcDmOp2K4plrvUrz2Mqa5OkyXR
UWT3AjEA4HSNcCzxKsyzYGHksB5ABwfY9k7OOJDiyNhH1iEFJeizRjqVkChPR8/f
JqDmiO+J
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICYTCCAeWgAwIBAgIEHmXAPTAMBggqhkjOPQQDAwUAMGgxCzAJBgNVBAYTAkNO
MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh
bTEoMCYGA1UEAxMfT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gUm9vdCBDQTAeFw0y
MTAyMDIxMjE1MzJaFw00OTEyMzExMjE1MzJaMGMxCzAJBgNVBAYTAkNOMRQwEgYD
VQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVhbTEjMCEG
A1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAAQhnu7Hna8XNa2KyqRf5+lBJScE4xqf89N0g0OuqAb2re8nGsvWkw26
uDekfnBYicd+G3Cydqa2zFIwV7Talyg2ULW3r8KbGpyl84mJEPPRmCGJ+H9gtCsf
+OrJ4Y76LVWjYzBhMB8GA1UdIwQYMBaAFBc6EKGrGXzlAE+s0Zgnsphadw7NMA8G
A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTbhrciFtUL
oUu33SV7ufEFfaItRzAMBggqhkjOPQQDAwUAA2gAMGUCMG3cXjiDmXTvf7D4Omhf
qcc2nuO+EMfWE+N9ZhBP5UhV34mAGWi3SfLU6rcV0urWEQIxAMYIb3epOnKhUrcm
Lfu1WKzFlpYQwmw73RaCHP2I3k6NcuWOYeNwWXSNZ8o0nzvaLg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,9 @@
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBMzCB2AIBADBGMQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkw
BwYDVQQKEwAxCTAHBgNVBAsTADENMAsGA1UEAxMERGVtbzBZMBMGByqGSM49AgEG
CCqGSM49AwEHA0IABKZosHNfe+WW7xq9cGZ0uqexn3+weRCBDHAY29wJJDpNEwNc
cWmliQfnvSUJ+xoEb2Byg8MSd+QgrK3efKUmdvagMDAuBgkqhkiG9w0BCQ4xITAf
MB0GA1UdDgQWBBRG9VxvjDGQKMLP8s0zkPN7abWzTDAMBggqhkjOPQQDAgUAA0gA
MEUCIQDSdtMqszdXYs5AzSdkymgOf3NTRZTuX7ecwjstG+mgLQIgPNQkz4d9NCes
EdcyujJfyZiCjQbvfPT/6uW3gluvLSI=
-----END NEW CERTIFICATE REQUEST-----

BIN
demo/js/signature/demo.p12 Normal file

Binary file not shown.

BIN
demo/js/signature/demo.p7b Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
.~猥<>3メV1Rィiノ"

View File

@ -0,0 +1 @@
̒Ln~3<02><>9<EFBFBD><39><EFBFBD>

View File

@ -0,0 +1 @@
áKľŃDho@­ş;ćT9

View File

@ -0,0 +1 @@
乏ク=(rX婀エ_

72
hiperf.gni Normal file
View File

@ -0,0 +1,72 @@
# Copyright (C) 2021 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.
hiperf_path = "//developtools/hiperf"
innerkits_path = "${hiperf_path}/interfaces/innerkits"
kits_path = "${hiperf_path}/interfaces/kits"
declare_args() {
hiperf_target_host=false
hiperf_target_static=false
hiperf_test_coverage=false
hiperf_sanitize=false
hiperf_check_time=false
hiperf_use_libunwind=true
hiperf_debug=true
hiperf_code_analyze=false
hiperf_use_syspara=true
have_common_tools=true
}
code_check_flag = [
"-Wformat",
"-Wall",
"-Wextra",
"-Werror",
"-Wmissing-field-initializers",
"-Wuninitialized",
"-Wunused-parameter",
"-Wunused-variable",
"-Wnull-pointer-arithmetic",
"-Wunused-lambda-capture",
"-Wuser-defined-warnings",
"-Wenum-compare-switch",
"-Wunneeded-internal-declaration",
"-Wundefined-var-template",
"-Wnonportable-include-path",
"-Wformat-extra-args",
"-Wsign-compare",
"-Wreturn-stack-address",
]
code_analyze_flag = [
"--analyze",
"-Xanalyzer",
"-analyzer-checker=cplusplus",
"-Xanalyzer",
"-analyzer-checker=core",
"-Xanalyzer",
"-analyzer-checker=security",
"-Xanalyzer",
"-analyzer-checker=unix",
]
if(is_double_framework && target_cpu == "arm64") {
# format specifies type 'long' but the argument has type
# 'std::__1::chrono::duration<long long, std::__1::ratio<1, 1000> >::rep' (aka 'long long')
# [-Werror,-Wformat]
code_check_flag += ["-Wno-format"]
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ARM_RAW_EVENT_TYPE_TABLE_H
#define ARM_RAW_EVENT_TYPE_TABLE_H
// clang-format off
{0x0, "raw-sw-incr"},
{0x1, "raw-l1-icache-refill"},
{0x2, "raw-l1-itlb-refill"},
{0x3, "raw-l1-dcache-refill"},
{0x4, "raw-l1-dcache"},
{0x5, "raw-l1-dtlb-refill"},
{0x6, "raw-load-retired"},
{0x7, "raw-store-retired"},
{0x8, "raw-instruction-retired"},
{0x9, "raw-exception-taken"},
{0xa, "raw-exception-return"},
{0xb, "raw-cid-write-retired"},
{0xc, "raw-pc-write-retired"},
{0xd, "raw-br-immed-retired"},
{0xe, "raw-br-return-retired"},
{0xf, "raw-unaligned-ldst-retired"},
{0x10, "raw-br-mis-pred"},
{0x11, "raw-cpu-cycles"},
{0x12, "raw-br-pred"},
{0x13, "raw-mem-access"},
{0x14, "raw-l1-icache"},
{0x15, "raw-l1-dcache-wb"},
{0x16, "raw-l2-dcache"},
{0x17, "raw-l2-dcache-refill"},
{0x18, "raw-l2-dcache-wb"},
{0x19, "raw-bus-access"},
{0x1a, "raw-memory-error"},
{0x1b, "raw-inst-spec"},
{0x1c, "raw-ttbr-write-retired"},
{0x1d, "raw-bus-cycles"},
{0x1f, "raw-l1-dcache-allocate"},
{0x20, "raw-l2-dcache-allocate"},
{0x21, "raw-br-retired"},
{0x22, "raw-br-mis-pred-retired"},
{0x23, "raw-stall-frontend"},
{0x24, "raw-stall-backend"},
{0x25, "raw-l1-dtlb"},
{0x26, "raw-l1-itlb"},
{0x27, "raw-l2-icache"},
{0x28, "raw-l2-icache-refill"},
{0x29, "raw-l3-dcache-allocate"},
{0x2a, "raw-l3-dcache-refill"},
{0x2b, "raw-l3-dcache"},
{0x2c, "raw-l3-dcache-wb"},
{0x2d, "raw-l2-dtlb-refill"},
{0x2e, "raw-l2-itlb-refill"},
{0x2f, "raw-l2-dtlb"},
{0x30, "raw-l2-itlb"},
#endif

139
include/callstack.h Executable file
View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_CALLSTACK_H
#define HIPERF_CALLSTACK_H
#if HAVE_LIBUNWIND
// for libunwind.h empty struct has size 0 in c, size 1 in c++
#define UNW_EMPTY_STRUCT uint8_t unused;
#include <libunwind.h>
#endif
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
#if !is_mingw
#include <sys/mman.h>
#endif
#include "hashlist.hpp"
#include "register.h"
#include "utilities.h"
#include "virtual_thread.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
const int MAX_CALL_FRAME_EXPEND_CYCLE = 10;
const size_t MAX_CALL_FRAME_EXPEND_CACHE_SIZE = 10;
const size_t MAX_CALL_FRAME_UNWIND_SIZE = 30;
// if ip is 0 , 1 both not usefule
const uint64_t BAD_IP_ADDRESS = 2;
#if HAVE_LIBUNWIND
struct UnwindInfo;
#endif
class CallStack {
public:
CallStack();
~CallStack();
bool UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum,
const u8 *stack, u64 stackSize, std::vector<CallFrame> &,
size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE);
size_t ExpendCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expendLimit = 1u);
private:
uint64_t stackPoint_ = 0;
uint64_t stackEnd_ = 0;
u64 *regs_ = nullptr; // not const , be cause we will fix it for arm64 cpu in UpdateRegForABI
u64 regsNum_ = 0;
const u8 *stack_ = nullptr;
u64 stackSize_ = 0;
void LogFrame(const std::string msg, const std::vector<CallFrame> &frames);
size_t ExpendCallStack(std::vector<CallFrame> &newCallFrames,
const std::vector<CallFrame> &cachedCallFrames, size_t expendLimit);
// we have a cache for all thread
std::map<pid_t, HashList<uint64_t, std::vector<CallFrame>>> cachedCallFramesMap_;
bool GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const;
ArchType arch_ = ArchType::UNSUPPORT;
#if HAVE_LIBUNWIND
static bool ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, unw_word_t addr,
unw_word_t *data);
static const std::string GetUnwErrorName(int error);
static void dumpUDI(unw_dyn_info_t &di);
static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, const MemMapItem &mmap,
const VirtualThread &thread);
static int FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
int need_unwind_info, void *arg);
static int AccessMem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valuePoint,
int writeOperation, void *arg);
static int AccessReg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valuePoint,
int writeOperation, void *arg);
static void PutUnwindInfo(unw_addr_space_t as, unw_proc_info_t *pi, void *arg);
static int AccessFpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val,
int writeOperation, void *arg);
static int GetDynInfoListAaddr(unw_addr_space_t as, unw_word_t *dil_vaddr, void *arg);
static int Resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg);
static int getProcName(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len,
unw_word_t *offp, void *arg);
static int FindUnwindTable(SymbolsFile *symbolsFile, const MemMapItem &mmap,
UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
unw_proc_info_t *pi, int need_unwind_info, void *arg);
void UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callFrames, size_t maxStackLevel);
std::unordered_map<pid_t, unw_addr_space_t> unwindAddrSpaceMap_;
using dsoUnwDynInfoMap = std::unordered_map<std::string, std::optional<unw_dyn_info_t>>;
std::unordered_map<pid_t, dsoUnwDynInfoMap> unwindDynInfoMap_;
using unwMemoryCache = std::unordered_map<unw_word_t, unw_word_t>;
std::unordered_map<pid_t, unwMemoryCache> porcessMemoryMap_;
unw_accessors_t accessors_ = {
.find_proc_info = FindProcInfo,
.put_unwind_info = PutUnwindInfo,
.get_dyn_info_list_addr = GetDynInfoListAaddr,
.access_mem = AccessMem,
.access_reg = AccessReg,
.access_fpreg = AccessFpreg,
.resume = Resume,
.get_proc_name = getProcName,
};
bool DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
size_t maxStackLevel);
#endif
FRIEND_TEST(CallStackTest, ExpendCallStackFullCache);
FRIEND_TEST(CallStackTest, LibUnwindEmptyFunc);
FRIEND_TEST(CallStackTest, GetUnwErrorName);
};
#if HAVE_LIBUNWIND
struct UnwindInfo {
const VirtualThread &thread;
const u64 *regs;
size_t regNumber;
ArchType arch;
CallStack &callStack;
};
#endif
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

32
include/command.h Executable file
View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_COMMAND_H
#define HIPERF_COMMAND_H
#include <string>
#include <vector>
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class Command {
public:
static std::string fullArgument;
static bool DispatchCommands(std::vector<std::string> arguments);
static bool DispatchCommand(std::string argument);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

371
include/debug_logger.h Executable file
View File

@ -0,0 +1,371 @@
/*
* Copyright (c) 2021 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.
*
*/
#ifndef HIPERF_DEBUG_H
#define HIPERF_DEBUG_H
#include <chrono>
#include <cstring>
#include <map>
#include <memory>
#include <mutex>
#include <sstream>
#include <stdio.h>
#include <string>
#include <unistd.h>
#include <gtest/gtest_prod.h>
#if !is_mingw
#include <sys/syscall.h>
#undef gettid
#define gettid() syscall(SYS_gettid)
#else
#include "windows.h"
inline long gettid()
{
return GetCurrentThreadId();
}
#endif // !is_mingw
namespace OHOS {
namespace Developtools {
namespace HiPerf {
enum DebugLevel {
LEVEL_MUCH = 1,
LEVEL_VERBOSE,
LEVEL_DEBUG,
LEVEL_INFO,
LEVEL_WARNING,
LEVEL_ERROR,
LEVEL_FATAL,
LEVEL_STDOUT, // printf
LEVEL_MAX, // max
};
#ifdef HIPERF_DEBUG
#if is_ohos || is_double_framework
const std::string DEFAULT_LOG_PATH = "/data/local/tmp/hiperf_log.txt";
#elif is_mingw
const std::string DEFAULT_LOG_PATH = ".\\hiperf_log.txt";
#elif is_linux
const std::string DEFAULT_LOG_PATH = "hiperf_log.txt";
#else
#error unkow os
#endif
#define HILOG_BASE_TAG "HILOG"
#ifndef HILOG_TAG
#define HILOG_TAG ""
#define HILOG_TAG_NAME HILOG_BASE_TAG
#else
#define HILOG_TAG_NAME HILOG_BASE_TAG "_" HILOG_TAG
#endif
#define SHORT_FILENAME \
(__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
const std::map<DebugLevel, const std::string> DebugLevelMap = {
{LEVEL_MUCH, "M"}, {LEVEL_VERBOSE, "V"}, {LEVEL_DEBUG, "D"}, {LEVEL_INFO, "I"},
{LEVEL_WARNING, "W"}, {LEVEL_ERROR, "E"}, {LEVEL_FATAL, "F"},
};
constexpr const int LOG_BUFFER_SIZE = 4 * 1024 * 1024;
class DebugLogger {
public:
DebugLogger();
~DebugLogger();
static DebugLogger *GetInstance();
DebugLevel SetLogLevel(DebugLevel debugLevel);
bool SetMixLogOutput(bool enable);
bool SetLogPath(const std::string &logPath);
void SetLogTags(const std::string &tags);
int Log(DebugLevel level, const std::string &logTag, const char *fmt, ...) const
__attribute__((format(printf, 4, 5)));
// for class, pointer need add 1 offset (first one is *this)
bool EnableHiLog(bool = true);
DebugLevel GetLogLevel() const
{
return debugLevel_;
};
void Disable(bool disable = true);
static bool logDisabled_;
#ifdef HIPERF_DEBUG_TIME
mutable size_t logCount_ = 0;
mutable std::chrono::microseconds logTimes_ = std::chrono::microseconds::zero();
mutable std::chrono::microseconds logWriteTimes_ = std::chrono::microseconds::zero();
mutable std::chrono::microseconds logSprintfTimes_ = std::chrono::microseconds::zero();
#endif
private:
bool ShouldLog(DebugLevel debugLevel, const std::string &logTag) const;
DebugLevel GetLogLevelByName(const std::string &) const;
DebugLevel GetLogLevelByTag(const std::string &) const;
const std::string GetLogLevelName(DebugLevel) const;
int HiLog(std::string &buffer) const;
static std::unique_ptr<DebugLogger> logInstance_;
std::string logFileBuffer_;
mutable std::mutex logMutex_;
static DebugLevel debugLevel_;
const std::chrono::steady_clock::time_point timeStamp_;
bool OpenLog();
FILE *file_ = nullptr;
bool mixLogOutput_ = false; // log mix to std
bool enableHilog_ = false;
bool exitOnFatal_ = true;
std::string logPath_;
std::map<std::string, DebugLevel> logTagLevelmap_;
friend class OptionDebugTest;
friend class DebugLoggerTest;
FRIEND_TEST(DebugLoggerTest, SetLogTags);
FRIEND_TEST(DebugLoggerTest, Disable);
};
#ifdef HIPERF_DEBUG_PRINTF
#ifndef printf
#define printf(format, ...) \
do { \
std::printf(format, ##__VA_ARGS__); \
DebugLogger::GetInstance()->Log(LEVEL_STDOUT, HILOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#endif
#ifndef perror
#define perror(format, ...) \
do { \
std::perror(format); \
DebugLogger::GetInstance()->Log(LEVEL_STDOUT, HILOG_TAG, format "<%d:%s>\n", \
##__VA_ARGS__, errno, strerror(errno)); \
} while (0)
#endif
#endif
class ScopeDebugLevel {
public:
ScopeDebugLevel(DebugLevel level, bool mix = false);
~ScopeDebugLevel();
private:
DebugLevel savedDebugLevel_;
bool savedMixOutput_ = false; // log mix to std
};
#define TempMixLogLevel(level) ScopeDebugLevel tempLogLevel(level, true)
#define LOG_LEVEL(LEVEL) LOG_##LEVEL
#define LOG_LEVEL_MUCH "M:"
#define LOG_LEVEL_VERBOSE "V:"
#define LOG_LEVEL_DEBUG "D:"
#define LOG_LEVEL_INFO "I:"
#define LOG_LEVEL_WARNING "W:"
#define LOG_LEVEL_ERROR "E:"
#define LOG_LEVEL_FATAL "F:"
#ifndef HLOG
#define HLOG(level, format, ...) \
do { \
if (__builtin_expect(!DebugLogger::logDisabled_, false)) { \
DebugLogger::GetInstance()->Log( \
level, HILOG_TAG, \
HILOG_TAG_NAME "/" LOG_LEVEL(level) "<%ld>[%s:%d]%s:" format "\n", gettid(), \
SHORT_FILENAME, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
} \
} while (0)
#endif
// only log first n times
#ifndef HLOGV_FIRST
#define HLOGV_FIRST(first, format, ...) \
do { \
static int limit = first; \
if (limit > 0) { \
HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__); \
if (--limit == 0) { \
HLOG(LEVEL_VERBOSE, " nexttime log will be suppressed..."); \
} \
} \
} while (0)
#endif
#ifndef HLOGV_FIRST_LOCAL
#define HLOGV_FIRST_LOCAL(local_limit, format, ...) \
{ \
if (local_limit != 0) { \
HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__); \
if (local_limit > 0 && --local_limit == 0) { \
HLOG(LEVEL_VERBOSE, " nexttime log will be suppressed..."); \
} \
} \
}
#endif
#ifndef HLOGV
#define HLOGV_IF(condition, format, ...) \
if (condition) { \
HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__) \
}
#define HLOGVVV HLOGV
#endif
#ifndef HLOGDUMMY
#define HLOGDUMMY(format, ...) while (0)
#endif
#ifndef HLOGM
#define HLOGM(format, ...) HLOG(LEVEL_MUCH, format, ##__VA_ARGS__)
#define HLOGMMM HLOGM
#endif
#ifndef HLOGV
#define HLOGV(format, ...) HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__)
#endif
#ifndef HLOGD
#define HLOGD(format, ...) HLOG(LEVEL_DEBUG, format, ##__VA_ARGS__)
#define HLOGDDD HLOGM
#endif
#ifndef HLOGI
#define HLOGI(format, ...) HLOG(LEVEL_INFO, format, ##__VA_ARGS__)
#endif
#ifndef HLOGW
#define HLOGW(format, ...) HLOG(LEVEL_WARNING, format, ##__VA_ARGS__)
#endif
#ifndef HLOGE
#define HLOGE(format, ...) HLOG(LEVEL_ERROR, format, ##__VA_ARGS__)
#endif
#ifndef HLOGEP
#define HLOGEP(format, ...) \
HLOG(LEVEL_ERROR, format "(errno %d:%s)", ##__VA_ARGS__, errno, strerror(errno))
#endif
#ifndef HLOGF
#define HLOGF(format, ...) \
HLOG(LEVEL_FATAL, "FATAL error at %s:%d " format, __FILE__, __LINE__, ##__VA_ARGS__)
#endif
#ifndef HLOG_ASSERT_MESSAGE
#define HLOG_ASSERT_MESSAGE(condition, format, ...) \
if (!(condition)) { \
HLOG(LEVEL_FATAL, " assert failed: '%s' " format, #condition, ##__VA_ARGS__); \
}
#endif
#ifndef HLOG_ASSERT
#define HLOG_ASSERT(condition) HLOG_ASSERT_MESSAGE(condition, "")
#endif
#undef assert
class LogMessage {
public:
LogMessage(DebugLevel level = LEVEL_VERBOSE, bool showError = false)
: level_(level), showError_(showError)
{
}
std::ostream &Stream()
{
return buffer_;
}
~LogMessage()
{
if (!DebugLogger::logDisabled_) {
if (!showError_) {
DebugLogger::GetInstance()->Log(level_, HILOG_TAG, "%s\n", buffer_.str().c_str());
} else {
DebugLogger::GetInstance()->Log(level_, HILOG_TAG, "%s (errno %d:%s)\n",
buffer_.str().c_str(), errno, strerror(errno));
}
}
}
private:
DebugLevel level_;
bool showError_;
std::ostringstream buffer_;
};
#define HLOGMESSAGE(level, error) \
LogMessage(level, error).Stream() \
<< HILOG_TAG_NAME << "/" << LOG_LEVEL(level) << "<" << gettid() << ">[" << SHORT_FILENAME \
<< ":" << __LINE__ << "]" << __FUNCTION__ << ":"
#define HLOGS(level) HLOGMESSAGE(level, false)
#define HLOGSP(level) HLOGMESSAGE(level, true)
#else
#define HLOGS(...) std::ostringstream()
#define HLOGSP(...) std::ostringstream()
#define HLOGDUMMY(...) \
do { \
} while (0)
#define HLOGEP(...) \
do { \
} while (0)
#define HLOGM(...) \
do { \
} while (0)
#define HLOGMMM(...) \
do { \
} while (0)
#define HLOGV(...) \
do { \
} while (0)
#define HLOGVVV(...) \
do { \
} while (0)
#define HLOGD(...) \
do { \
} while (0)
#define HLOGDDD(...) \
do { \
} while (0)
#define HLOGI(...) \
do { \
} while (0)
#define HLOGW(...) \
do { \
} while (0)
#define HLOGE(...) \
do { \
} while (0)
#define HLOGF(...) \
do { \
} while (0)
#define HLOG_ASSERT_MESSAGE(...) \
do { \
} while (0)
#define HLOG_ASSERT(...) \
do { \
} while (0)
class ScopeDebugLevel {
public:
ScopeDebugLevel(DebugLevel level, bool mix = false) {};
};
#endif
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif // _HIPERF_DEBUG_H_

193
include/dwarf_encoding.h Executable file
View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef DWARF_ENCODING_H
#define DWARF_ENCODING_H
#include "utilities.h"
// now we only support 64 bit.
using uleb128_t = uint64_t;
using sleb128_t = int64_t;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
static constexpr const int LEB_BYTE_EFFECTIVE_LENGTH = 7;
static constexpr const int SIGN_BIT_OF_BYTE = 0x40;
static constexpr const int MAX_VALUE_OF_BYTE = 0x7f;
static constexpr const int MORE_BIT_OF_BYTE = 0x80;
/*
10.5.1. DWARF Exception Header Encoding
The DWARF Exception Header Encoding is used to describe the type of data used in the .eh_frame and
.eh_frame_hdr section. The upper 4 bits indicate how the value is to be applied. The lower 4 bits
indicate the format of the data.
using dw_encode_t = unsigned char; // 4 bits + 4 bits
*/
using dw_encode_t = unsigned char; // 4 bits + 4 bits
// Table 10-5. DWARF Exception Header value format
enum DW_EH_PE_VF {
DW_EH_PE_absptr = 0x00, // a literal pointer whose size is determined by the architecture.
DW_EH_PE_uleb128 = 0x01, // Unsigned value is encoded using the Little Endian Base 128 (LEB128)
DW_EH_PE_udata2 = 0x02, // A 2 bytes unsigned value.
DW_EH_PE_udata4 = 0x03, // A 4 bytes unsigned value.
DW_EH_PE_udata8 = 0x04, // An 8 bytes unsigned value.
DW_EH_PE_sleb128 = 0x09, // Signed value is encoded using the Little Endian Base 128(LEB128)
DW_EH_PE_sdata2 = 0x0A, // A 2 bytes signed value.
DW_EH_PE_sdata4 = 0x0B, // A 4 bytes signed value.
DW_EH_PE_sdata8 = 0x0C, // An 8 bytes signed value.
};
// Table 10-6. DWARF Exception Header application
enum DW_EH_PE_A {
DW_EH_PE_nothing = 0x00, // nothing to do
DW_EH_PE_pcrel = 0x10, // relative to the current program counter.
DW_EH_PE_textrel = 0x20, // relative to the beginning of the .text section.
DW_EH_PE_datarel = 0x30, // relative to the beginning of the .got or .eh_frame_hdr section.
DW_EH_PE_funcrel = 0x40, // relative to the beginning of the function.
DW_EH_PE_aligned = 0x50, // aligned to an address unit sized boundary.
DW_EH_PE_omit = 0xff, // indicate that no value ispresent.
};
const std::map<dw_encode_t, size_t> DWFormatSizeMap = {
#ifdef ARM
{DW_EH_PE_absptr, 4},
#else
{DW_EH_PE_absptr, 8},
#endif
#ifdef NOT_USE
{DW_EH_PE_uleb128, sizeof(char) * 128},
#endif
{DW_EH_PE_udata2, sizeof(char) * 2},
{DW_EH_PE_udata4, sizeof(char) * 4},
{DW_EH_PE_udata8, sizeof(char) * 8},
#ifdef NOT_USE
{DW_EH_PE_sleb128, sizeof(char) * 128},
#endif
{DW_EH_PE_sdata2, sizeof(char) * 2},
{DW_EH_PE_sdata4, sizeof(char) * 4},
{DW_EH_PE_sdata8, sizeof(char) * 8},
};
template<class T>
uint64_t dwReadAnyTypeData(const unsigned char *&buffer, T)
{
T value;
if (memcpy_s(&value, sizeof(T), buffer, sizeof(T)) != 0) {
return 0;
}
buffer += sizeof(T);
return static_cast<uint64_t>(value);
}
class DwarfEncoding {
public:
DwarfEncoding(dw_encode_t dw, const unsigned char *&data, uint64_t vaddrBase = 0,
uint64_t vaddrPC = 0, uint64_t vaddrText = 0);
const std::string ToString() const;
const unsigned char *GetEnd() const;
const unsigned char *GetData() const;
size_t GetSize() const;
uint64_t GetValue() const;
uint64_t GetAppliedValue() const;
bool IsOmit() const;
private:
dw_encode_t dw_;
const unsigned char *data_;
uint64_t vaddrBase_ = 0;
uint64_t vaddrPC_ = 0;
uint64_t vaddrText_ = 0;
uint64_t value_[2] = {0, 0};
dw_encode_t Format() const;
dw_encode_t Application() const;
uint64_t ReadValue(const unsigned char *&data) const;
const std::string FormatName() const;
const std::string ApplicationName() const;
};
/*
Linux Standard Base Core Specification 4.1
Chapter 10. Object Format
10.6.2. The .eh_frame_hdr section
Table 10-11. .eh_frame_hdr Section Format
Encoding Field
unsigned byte version
unsigned byte eh_frame_ptr_enc
unsigned byte fde_count_enc
unsigned byte table_enc
encoded eh_frame_ptr
encoded fde_count
binary search table
*/
struct binary_search_table {
uint64_t ipVaddrOffset;
uint64_t fdeVaddrOffset;
};
struct eh_frame_hdr {
// Version of the .eh_frame_hdr format. This value shall be 1.
dw_encode_t version;
// The encoding format of the eh_frame_ptr field.
dw_encode_t eh_frame_ptr_enc;
// The encoding format of the fde_count field. A value of DW_EH_PE_omit indicates the binary
// search table is not present.
dw_encode_t fde_count_enc;
// The encoding format of the entries in the binary search table. A value of DW_EH_PE_omit
// indicates the binary search table is not present.
dw_encode_t table_enc;
// The encoded value of the pointer to the start of the .eh_frame section.
/*
dw_encode_t eh_frame_ptr
dw_encode_t fde_count
*/
// A binary search table containing fde_count entries. Each entry of the table consist of two
// encoded values, the initial location, and the address. The entries are sorted in an
// increasing order by the initial location value.
/*
* struct {
* encoded start_ip
* encoded fde_addr
* } binary_search_table[fde_count]
*/
unsigned char encode_data[0];
} PACKED;
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

310
include/elf_parser.h Executable file
View File

@ -0,0 +1,310 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ELF_PARSER_H_
#define ELF_PARSER_H_
#include <cassert>
#include <cstring>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#if !is_ohos
// this is not good enough
#include <../musl/include/elf.h>
#else
#include <elf.h>
#endif
#include <fcntl.h>
#include <stdint.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>
#include "debug_logger.h"
#include "noncopyable.h"
#include "utilities.h"
#if !is_mingw
#include <sys/mman.h>
#include <sys/stat.h>
#endif
namespace OHOS {
namespace Developtools {
namespace HiPerf {
namespace ELF {
using namespace std::string_literals;
constexpr std::size_t ehdr32Size {52};
constexpr std::size_t ehdr64Size {64};
constexpr std::size_t shdr32Size {40};
constexpr std::size_t shdr64Size {64};
constexpr std::size_t phdr32Size {32};
constexpr std::size_t phdr64Size {56};
constexpr std::size_t symEnt32Size {16};
constexpr std::size_t symEnt64Size {24};
class ElfHeader {
public:
static std::unique_ptr<ElfHeader> MakeUnique(unsigned char * const ehdrBuf,
const std::size_t bufSize);
bool Init(unsigned char * const ehdrBuf, const std::size_t bufSize);
unsigned char ehdrIdent_[EI_NIDENT];
uint16_t type_;
uint16_t machine_;
uint16_t ehdrSize_;
uint16_t phdrEntSize_;
uint16_t phdrNumEnts_;
uint16_t shdrEntSize_;
uint16_t shdrNumEnts_;
uint16_t shdrStrTabIdx_;
uint32_t elfVersion_;
uint32_t ehdrFlags_;
uint64_t prgEntryVaddr_;
uint64_t phdrOffset_;
uint64_t shdrOffset_;
private:
explicit ElfHeader() = default;
bool ParseElf32Header(unsigned char * const ehdrBuf, const std::size_t bufSize);
bool ParseElf64Header(unsigned char * const ehdrBuf, const std::size_t bufSize);
static inline void DumpEhdrBuf(const char * const ehdrBuf, const std::size_t bufSize)
{
const std::string fileName {"ehdr_buffer_dump"};
std::ofstream ofs {fileName, std::ios::binary};
if (ofs.is_open()) {
ofs.write(ehdrBuf, bufSize);
}
}
};
class ProgramHeader {
public:
static std::unique_ptr<ProgramHeader> MakeUnique(char * const phdrBuf, const size_t bufSize);
inline bool Init(char * const phdrBuf, const size_t bufSize)
{
if (bufSize == phdr32Size and ParsePrgHeader32(phdrBuf)) {
return true;
}
if (bufSize == phdr64Size and ParsePrgHeader64(phdrBuf)) {
return true;
}
HLOGE("parse program header failed, program header buffer dumped");
return false;
}
uint32_t type_;
uint32_t flags_;
uint64_t offset_;
uint64_t vaddr_;
uint64_t paddr_;
uint64_t fileSize_;
uint64_t memSize_;
uint64_t secAlign_;
private:
explicit ProgramHeader() = default;
bool ParsePrgHeader32(char * const phdrBuf);
bool ParsePrgHeader64(char * const phdrBuf);
static inline void DumpPhdrBuf(const char * const phdrBuf, const std::size_t bufSize)
{
const std::string fileName {"phdr_buffer_dump"};
std::ofstream ofs {fileName, std::ios::binary};
if (ofs.is_open()) {
ofs.write(phdrBuf, bufSize);
}
}
};
class SectionHeader {
public:
static std::unique_ptr<SectionHeader> MakeUnique(char * const shdrBuf, const size_t bufSize,
const size_t index);
inline bool Init(char * const shdrBuf, const size_t bufSize, const size_t index)
{
secIndex_ = index;
if (bufSize == shdr32Size and ParseSecHeader32(shdrBuf)) {
return true;
}
if (bufSize == shdr64Size and ParseSecHeader64(shdrBuf)) {
return true;
}
HLOGE("parse section header failed, section header buffer dumped");
return false;
}
uint32_t nameIndex_;
uint32_t link_;
uint32_t info_;
uint64_t secFlags_;
uint64_t secVaddr_;
uint64_t fileOffset_;
uint64_t secSize_;
uint64_t secAddrAlign_;
uint64_t secEntrySize_;
uint64_t secType_;
uint32_t secIndex_;
std::string secTypeName_;
private:
explicit SectionHeader() = default;
bool ParseSecHeader32(char * const shdrBuf);
bool ParseSecHeader64(char * const shdrBuf);
static inline void DumpShdrBuf(const char * const shdrBuf, const std::size_t bufSize)
{
const std::string fileName {"shdr_buffer_dump"};
std::ofstream ofs {fileName, std::ios::binary};
if (ofs.is_open()) {
ofs.write(shdrBuf, bufSize);
}
}
};
class ElfSymbol {
public:
static std::unique_ptr<ElfSymbol> MakeUnique(char * const symBuf, const std::size_t bufSize);
inline bool Init(char * const symBuf, const std::size_t bufSize)
{
if (bufSize == symEnt32Size and ParseElf32Symbol(symBuf)) {
return true;
}
if (bufSize == symEnt64Size and ParseElf64Symbol(symBuf)) {
return true;
}
HLOGE("parse elf symbol failed, symbol buffer dumped");
return false;
}
uint16_t secIndex_;
uint32_t nameIndex_;
uint64_t symValue_;
uint64_t symSize_;
unsigned char symInfo_;
unsigned char symOtherInfo_;
private:
explicit ElfSymbol() = default;
bool ParseElf32Symbol(char * const symBuf);
bool ParseElf64Symbol(char * const symBuf);
static inline void DumpSymBuf(const char * const symBuf, const std::size_t bufSize)
{
const std::string fileName {"shdr_buffer_dump"};
std::ofstream ofs {fileName, std::ios::binary};
if (ofs.is_open()) {
ofs.write(symBuf, bufSize);
}
}
};
class SymbolTable {
public:
static std::unique_ptr<SymbolTable> MakeUnique(const std::string &symNamesStr,
const char * const secBuf,
const uint64_t secSize,
const uint64_t entrySize);
std::vector<std::unique_ptr<ElfSymbol>> symbols_;
private:
explicit SymbolTable(const std::string &symNamesStr) : symNamesStr_ {symNamesStr} {}
const std::string symNamesStr_ {};
};
class ElfFile : public Noncopyable {
public:
virtual ~ElfFile();
static std::unique_ptr<ElfFile> MakeUnique(const std::string &filename);
bool ParseFile();
bool ParseSymTable(const SectionHeader *shdr);
std::string GetSectionName(const uint32_t startIndex);
inline bool IsOpened() const
{
return fd_ != -1;
}
inline const char *GetStrPtr(uint32_t sh_link, uint32_t st_name)
{
for (const auto &shdrsItem : shdrs_) {
if (shdrsItem.second->secIndex_ == sh_link) {
if (mmap_ != MMAP_FAILED) {
char *elfFileBegin = (char *)mmap_;
return elfFileBegin + shdrsItem.second->fileOffset_ + st_name;
}
}
}
HLOGE("string not found sh_link %u st_name %d, mmap_ is %p", sh_link, st_name, mmap_);
return nullptr;
}
inline const unsigned char *GetSectionData(uint32_t shIndex)
{
for (const auto &shdrsItem : shdrs_) {
if (shdrsItem.second->secIndex_ == shIndex) {
if (mmap_ != MMAP_FAILED) {
const unsigned char *elfFileBegin = (const unsigned char *)mmap_;
return elfFileBegin + shdrsItem.second->fileOffset_;
}
}
}
HLOGE("string not found shIndex %u ", shIndex);
return nullptr;
}
using SecHeaderTableType = std::unordered_map<std::string, std::unique_ptr<SectionHeader>>;
using PrgHeaderTableType = std::vector<std::unique_ptr<ProgramHeader>>;
int fd_ {-1};
std::unique_ptr<ElfHeader> ehdr_ {nullptr};
SecHeaderTableType shdrs_ {};
PrgHeaderTableType phdrs_ {};
std::string secNamesStr_ {};
std::string symNamesStr_ {};
std::unique_ptr<SymbolTable> symTable_ {nullptr};
std::unique_ptr<SymbolTable> dynSymTable_ {nullptr};
protected:
// for fuzz test we make a virtual function
virtual ssize_t ReadFile(void *buf, size_t count)
{
return read(fd_, buf, count);
};
explicit ElfFile(const std::string &filename);
private:
bool ParseElfHeader();
bool ParsePrgHeaders();
bool ParseSecNamesStr();
bool ParseSecHeaders();
bool ParseSymNamesStr();
bool ParseSymTable(const std::string = ".symtab");
bool ParseDynSymTable();
void *mmap_ = MMAP_FAILED;
uint64_t mmapSize_ = 0;
};
} // namespace ELF
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

225
include/hashlist.h Executable file
View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_HASHLIST_H
#define HIPERF_HASHLIST_H
#include <unordered_map>
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class Link {
public:
Link() = default;
~Link() = default;
Link(const Link &link) : prev_ {link.prev_}, next_ {link.next_} {}
Link(Link &&link) : prev_ {link.prev_}, next_ {link.next_}
{
link.prev_ = nullptr;
link.next_ = nullptr;
}
Link &operator=(const Link &link)
{
prev_ = link.prev_;
next_ = link.next_;
return *this;
}
Link &operator=(Link &&link)
{
prev_ = link.prev_;
link.prev_ = nullptr;
next_ = link.next_;
link.next_ = nullptr;
return *this;
}
Link *prev_ {nullptr};
Link *next_ {nullptr};
};
template<typename Key, typename Val>
class LinkNode {
public:
Link link_ {};
Key key_ {};
Val val_ {};
explicit LinkNode() = default;
~LinkNode() = default;
explicit LinkNode(const Key &key);
explicit LinkNode(const Key &key, const Val &val);
explicit LinkNode(const Key &key, Val &&val);
LinkNode(const LinkNode &node);
LinkNode(LinkNode &&node);
LinkNode &operator=(const LinkNode &node);
LinkNode &operator=(LinkNode &&node);
static LinkNode<Key, Val> *GetLinkNode(Val *pval);
static LinkNode<Key, Val> *GetLinkNode(Link *plink);
};
template<typename Key, typename Val>
class HashList {
public:
class Iterator {
public:
Iterator() = default;
~Iterator() = default;
explicit Iterator(LinkNode<Key, Val> *pnode, HashList *phashList);
explicit Iterator(const LinkNode<Key, Val> *pnode, const HashList *phashList);
Iterator(const Iterator &itr);
Iterator(Iterator &&itr);
Iterator &operator=(const Iterator &itr);
Iterator &operator=(Iterator &&itr);
Iterator &operator++() noexcept;
Iterator operator++(int) noexcept;
Iterator &operator--() noexcept;
Iterator operator--(int) noexcept;
bool operator<(const Iterator &itr) const noexcept;
bool operator==(const Iterator &itr) const noexcept;
Val &operator*();
const Val &operator*() const;
Val *operator->();
const Val *operator->() const;
void swap(HashList<Key, Val>::Iterator &other);
LinkNode<Key, Val> *GetNode() const
{
return pnode_;
}
private:
bool IsDangled() const noexcept
{
return phashList_ == nullptr;
}
LinkNode<Key, Val> *pnode_ {nullptr};
HashList *phashList_ {nullptr};
};
class ReverseIterator {
public:
ReverseIterator() = default;
~ReverseIterator() = default;
explicit ReverseIterator(LinkNode<Key, Val> *pnode, HashList *phashList);
explicit ReverseIterator(const LinkNode<Key, Val> *pnode, const HashList *phashList);
ReverseIterator(const ReverseIterator &itr);
ReverseIterator(ReverseIterator &&itr);
ReverseIterator &operator=(const ReverseIterator &itr);
ReverseIterator &operator=(ReverseIterator &&itr);
ReverseIterator &operator++() noexcept;
ReverseIterator operator++(int) noexcept;
ReverseIterator &operator--() noexcept;
ReverseIterator operator--(int) noexcept;
bool operator<(const ReverseIterator &itr) const noexcept;
bool operator==(const ReverseIterator &itr) const noexcept;
Val &operator*();
const Val &operator*() const;
Val *operator->();
const Val *operator->() const;
void swap(HashList<Key, Val>::ReverseIterator &other);
LinkNode<Key, Val> *GetNode()
{
return pnode_;
}
private:
bool IsDangled() const noexcept
{
return phashList_ == nullptr;
}
LinkNode<Key, Val> *pnode_ {nullptr};
HashList *phashList_ {nullptr};
};
public:
explicit HashList(const std::size_t numItem = 0);
~HashList();
HashList(const HashList &source) = delete;
HashList &operator=(const HashList &source) = delete;
HashList(HashList &&source);
HashList &operator=(HashList &&source);
// capacity
inline std::size_t size() const
{
return valueTab_.size();
}
inline bool empty() const
{
return (dataHead_.next_ == &dataHead_) and (dataHead_.prev_ == &dataHead_);
}
inline std::size_t capacity() const
{
return numItem_;
}
inline bool IsFull() const
{
return freeHead_.next_ == &freeHead_;
}
inline std::size_t count(const Key &key) const
{
return valueTab_.count(key);
}
int reserve(const std::size_t numItem);
// iterators
Iterator begin();
const Iterator cbegin() const;
Iterator end();
const Iterator cend() const;
ReverseIterator rbegin();
const ReverseIterator crbegin() const;
ReverseIterator rend();
const ReverseIterator crend() const;
// element access
Val &front();
const Val &front() const;
Val &back(bool prepend = false);
Val &operator[](const Key &key);
// lookup
Iterator find(const Key &key);
// modifiers
void push_front(const Key &key, const Val &val);
void push_front(const Key &key, Val &&val);
void push_back(const Key &key, const Val &val);
void push_back(const Key &key, Val &&val);
void pop_front();
void pop_back();
Iterator erase(const Key &key);
Iterator erase(const Iterator pos);
Iterator erase(const Iterator first, const Iterator last);
private:
void MoveToHead(LinkNode<Key, Val> *&pnode);
void MoveToTail(LinkNode<Key, Val> *&pnode);
bool MoveNode(const Iterator &pos, LinkNode<Key, Val> *&pnode);
LinkNode<Key, Val> *AllocateNode(const Key &key);
LinkNode<Key, Val> *AllocateNode(const Key &key, const Val &val);
LinkNode<Key, Val> *AllocateNode(const Key &key, Val &&val);
void ReclaimNode(LinkNode<Key, Val> *&pnode);
std::size_t numItem_ {0};
LinkNode<Key, Val> *pData_ {nullptr};
Link dataHead_ {};
Link freeHead_ {};
std::unordered_map<Key, LinkNode<Key, Val> *> valueTab_ {};
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

1000
include/hashlist.hpp Executable file

File diff suppressed because it is too large Load Diff

95
include/hiperf_hilog.h Normal file
View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_HILOG
#define HIPERF_HILOG
#ifndef CONFIG_NO_HILOG
#define HILOG_PUBLIC "{public}"
#define HILOG_NEWLINE ""
#else
#define HILOG_PUBLIC ""
#define HILOG_NEWLINE "\n"
#endif
#define FILENAME \
(__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
#define FORMATED(fmt, ...) \
"[%" HILOG_PUBLIC "s:%" HILOG_PUBLIC "d] %" HILOG_PUBLIC "s# " fmt HILOG_NEWLINE, FILENAME, \
__LINE__, __FUNCTION__, ##__VA_ARGS__
#ifndef CONFIG_NO_HILOG
#include "hilog/log.h"
#ifdef HIPERF_HILOGF
#undef HIPERF_HILOGF
#endif
#ifdef HIPERF_HILOGE
#undef HIPERF_HILOGE
#endif
#ifdef HIPERF_HILOGW
#undef HIPERF_HILOGW
#endif
#ifdef HIPERF_HILOGI
#undef HIPERF_HILOGI
#endif
#ifdef HIPERF_HILOGD
#undef HIPERF_HILOGD
#endif
// param of log interface, such as HIPERF_HILOGF.
enum HiperfModule {
MODULE_DEFAULT = 0,
MODULE_JS_NAPI,
MODULE_CPP_API,
};
static constexpr unsigned int BASE_HIPERF_DOMAIN_ID = 0xD000000;
static constexpr unsigned int BYTRACE_TAG = 0xd03301;
static constexpr OHOS::HiviewDFX::HiLogLabel HIPERF_HILOG_LABLE[] = {
{LOG_CORE, BYTRACE_TAG, "hiperf"},
{LOG_CORE, BYTRACE_TAG, "HiperfJSNAPI"},
{LOG_CORE, BYTRACE_TAG, "HiperfCPPAPI"},
};
// In order to improve performance, do not check the module range
#define HIPERF_HILOGF(module, ...) \
(void)OHOS::HiviewDFX::HiLog::Fatal(HIPERF_HILOG_LABLE[module], FORMATED(__VA_ARGS__))
#define HIPERF_HILOGE(module, ...) \
(void)OHOS::HiviewDFX::HiLog::Error(HIPERF_HILOG_LABLE[module], FORMATED(__VA_ARGS__))
#define HIPERF_HILOGW(module, ...) \
(void)OHOS::HiviewDFX::HiLog::Warn(HIPERF_HILOG_LABLE[module], FORMATED(__VA_ARGS__))
#define HIPERF_HILOGI(module, ...) \
(void)OHOS::HiviewDFX::HiLog::Info(HIPERF_HILOG_LABLE[module], FORMATED(__VA_ARGS__))
#define HIPERF_HILOGD(module, ...) \
(void)OHOS::HiviewDFX::HiLog::Debug(HIPERF_HILOG_LABLE[module], FORMATED(__VA_ARGS__))
#else
#define HIPERF_HILOGF(module, ...) printf(FORMATED(__VA_ARGS__))
#define HIPERF_HILOGE(module, ...) printf(FORMATED(__VA_ARGS__))
#define HIPERF_HILOGW(module, ...) printf(FORMATED(__VA_ARGS__))
#define HIPERF_HILOGI(module, ...) printf(FORMATED(__VA_ARGS__))
#define HIPERF_HILOGD(module, ...) printf(FORMATED(__VA_ARGS__))
#endif // CONFIG_NO_HILOG
#endif // HIPERF_HILOG

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_LIBREPORT_H
#define HIPERF_LIBREPORT_H
#if is_mingw
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __attribute__((visibility("default")))
#endif
#if is_ohos || is_double_framework
// error: '__cdecl' calling convention is not supported for this target
// [-Werror,-Wignored-attributes]
#define CDECL
#else
#define CDECL __cdecl
#endif
extern "C" {
/*
demo usage in script/loadlib_test.py
*/
// this is a loop back test
// will return the const char with caller send to us
DLL_EXPORT const char *CDECL EchoLoopBack(const char *);
// set the hiperf in debug mode , it will print some detail log for debug
// log will save in current dir
// return 0 means suucessed
DLL_EXPORT int CDECL SetDebug(bool enable);
// same as "hiperf report", will create a report file with ascii code
// parameter:
// perfFile the perf.data file path
// reportFile the output file path
// reportOptions options pass to report
// return 0 means suucessed
DLL_EXPORT int CDECL Report(const char *perfFile, const char *reportFile,
const char *reportOptions);
// same as "hiperf report --json"
// will output the report as json format
// our html template will read this for UI show
// parameter:
// perfFile the perf.data file path
// reportFile the output file path
// return 0 means suucessed
DLL_EXPORT int CDECL ReportJson(const char *perfFile, const char *reportFile);
// same as "hiperf report --json --symbol-dir <dir>"
// will output the report as json format and also unwind with --symbol-dir
// our html template will read this for UI show
// parameter:
// perfFile the perf.data file path
// reportFile the output file path
// return 0 means suucessed
DLL_EXPORT int CDECL ReportUnwindJson(const char *perfFile, const char *reportFile,
const char *symbolsDir);
// same as "hiperf report dump"
// must be give the the perf.data file path
// return 0 means suucessed
DLL_EXPORT int CDECL Dump(const char *);
// when give the perf.data file path
// will return a const char with this format
// [filepath,buildid],[filepath,buildid],[filepath,buildid],[filepath,buildid],....
// python use this for build id match
DLL_EXPORT const char *CDECL ReportGetSymbolFiles(const char *perfFile);
// when give the elf file path
// will return the buildId in this elf
// return "" when no buildId found
DLL_EXPORT const char *CDECL ReportGetBuildId(const char *elfPath);
// when give the elf file path
// will return the arch in this elf
// now support "arm","arm64","x86","x86_64"
// return machine id(const char in ascii) when not support
// return "unknow" when failed happend
DLL_EXPORT const char *CDECL ReportGetElfArch(const char *elfPath);
}
#endif

90
include/mem_map_item.h Executable file
View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef MEMMAPITEM_H
#define MEMMAPITEM_H
#include <sstream>
#include <string>
#include <sys/types.h>
class MemMapItem {
public:
uint64_t begin_ = 0;
uint64_t end_ = 0;
uint16_t type_ = 0; // rwx : PROT_READ | PROT_WRITE | PROT_EXEC
uint16_t flags = 0; // ps : MAP_PRIVATE | MAP_SHARED
uint64_t pageoffset_ = 0;
uint64_t major_ = 0;
uint64_t minor_ = 0;
ino_t inode = 0;
std::string name_;
MemMapItem() {}
MemMapItem(uint64_t begin, uint64_t end, uint64_t offset, const std::string &name)
: begin_(begin), end_(end), pageoffset_(offset), name_(name)
{
}
// use for find
inline bool operator==(const std::string &name) const
{
return name_ == name;
}
inline bool operator<(const MemMapItem &other) const
{
return end_ < other.end_;
}
uint64_t FileOffsetFromAddr(uint64_t addr) const
{
// real vaddr - real map begin = addr offset in section
// section offset + page off set = file offset
return addr - begin_ + pageoffset_;
}
// debug only
const std::string ToString() const
{
std::stringstream sstream;
sstream << "0x" << std::hex << begin_;
sstream << "-0x" << std::hex << end_;
sstream << " type 0x" << std::hex << type_;
sstream << " flags 0x" << std::hex << flags;
sstream << " pageoffset 0x" << std::hex << pageoffset_;
sstream << " " << name_;
return sstream.str();
}
static bool GreaterSort(const MemMapItem &a, const MemMapItem &b)
{
return (a.begin_ > b.begin_);
}
static bool LessSort(const MemMapItem &a, const MemMapItem &b)
{
return (a.begin_ < b.begin_);
}
// The range [first, last) must be partitioned with respect to the expression !(value < element)
// or !comp(value, element)
static bool ValueLessThan(uint64_t vaddr, const MemMapItem &a)
{
return vaddr <= a.begin_;
}
bool Contain(uint64_t addr) const
{
return addr >= begin_ and addr < end_;
}
};
#endif

26
include/noncopyable.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef NONCOPYABLE_H_
#define NONCOPYABLE_H_
class Noncopyable {
public:
Noncopyable() = default;
~Noncopyable() = default;
private:
Noncopyable(const Noncopyable &);
const Noncopyable &operator=(const Noncopyable &);
};
#endif

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef NONLINUX_MINGW64FIX_H
#define NONLINUX_MINGW64FIX_H
#if is_mingw
// in mingw 64 _pid_t define to int64
// but in linux it always int
#define _PID_T_
using _pid_t = int;
using pid_t = _pid_t;
#endif
#endif

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef BYTE_ORDER_H
#define BYTE_ORDER_H
#endif

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef NONLINUX_LINUX_IOCTL_H
#define NONLINUX_LINUX_IOCTL_H
#endif

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef NONLINUX_LINUX_TYPES_H
#define NONLINUX_LINUX_TYPES_H
#include <stdint.h>
/*
long long always 64
only in ILP64 , int is 64, but no OS use this
otherwise int is always 32
*/
using __s8 = char;
using __u8 = unsigned char;
using __s16 = short;
using __u16 = unsigned short;
using __s32 = int;
using __u32 = unsigned int;
using __s64 = long long;
using __u64 = unsigned long long;
#endif

140
include/option.h Executable file
View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_OPTION_H_
#define HIPERF_OPTION_H_
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "debug_logger.h"
#include "utilities.h"
using argsVector = std::vector<std::string>;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
namespace Option {
struct MainOption {
std::string help;
std::function<bool(std::vector<std::string> &)> callBackFunction;
};
// called from main
bool RegisterMainOption(const std::string &, const std::string &,
std::function<bool(std::vector<std::string> &)>);
void ClearMainOptions();
bool CheckOptionFormat(const std::string &optionName);
argsVector::iterator FindOption(argsVector &args, const std::string &optionName);
// some option function
bool GetValueFromString(const std::string &optionValue, const std::string &optionName, bool &value);
bool GetValueFromString(const std::string &optionValue, const std::string &optionName, int &);
bool GetValueFromString(const std::string &optionValue, const std::string &optionName,
float &value);
bool GetValueFromString(const std::string &optionValue, const std::string &optionName,
std::string &value);
bool GetValueFromString(const std::string &optionValue, const std::string &optionName,
std::vector<int> &value);
bool GetValueFromString(const std::string &optionValue, const std::string &optionName,
std::vector<std::string> &value);
bool GetOptionTrackedCommand(argsVector &args, std::vector<std::string> &trackedCommand);
/*
Return false to indicate that the parameter is illegal
The program should exit with an error.
Return true, indicating that the parameter is legal (but the user does not necessarily enter the
parameter)
*/
template<class T>
bool GetOptionValue(argsVector &args, std::string optionName, T &value)
{
// we need keep the ref if we got failed
// so we use a local value first.
T localValues = {};
if constexpr (std::is_same<T, std::vector<std::vector<std::string>>>::value) {
// try unitl failed.
while (true) {
if (!GetOptionValue(args, optionName, localValues.emplace_back())) {
printf("incorrect option %s\n", optionName.c_str());
return false; // format error
} else if (localValues.back().size() == 0) {
// if the last one we request is empty , we remove it
localValues.pop_back();
// nothing more needed
// we dont allow empty value
break;
}
}
if (localValues.size() > 0) {
value = localValues;
}
return true;
} else {
if (!CheckOptionFormat(optionName)) {
if (optionName.empty()) {
printf("unable to use empty option name!\n");
} else {
printf("format error. must use '-' at the begin of option '%s'!\n",
optionName.c_str());
}
return false; // something wrong
}
auto it = FindOption(args, optionName);
if (it == args.end()) {
HLOGV("not found option, return default value");
return true; // not found but also not error
} else {
it = args.erase(it);
// some specail case
if constexpr (std::is_same<T, bool>::value) {
// for bool we dont need get value.
// this always return true
GetValueFromString(optionName, optionName, value);
return true;
} else if (it == args.end()) {
// no value means failed
printf("option %s value missed\n", optionName.c_str());
return false;
} else if (GetValueFromString(*it, optionName, localValues)) {
// got some value
value = localValues;
args.erase(it);
return true;
} else {
// have value but conver failed.
printf("incorrect option value '%s'. View the usage with the --help option.\n",
(*it).c_str());
return false;
}
}
}
}
const MainOption *FindMainOption(std::string);
const std::map<std::string, std::unique_ptr<MainOption>> &GetMainOptions();
} // namespace Option
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

30
include/option_debug.h Executable file
View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_OPTION_DEBUG_H
#define HIPERF_OPTION_DEBUG_H
#include "debug_logger.h"
#include "utilities.h"
#include "option.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
void RegisterMainCommandDebug(void);
}
} // namespace Developtools
} // namespace OHOS
#endif

435
include/perf_event_record.h Executable file
View File

@ -0,0 +1,435 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_PERF_EVENT_RECORD_H
#define HIPERF_PERF_EVENT_RECORD_H
#include <atomic>
#include <chrono>
#include <map>
#include <memory>
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
#include <linux/perf_event.h>
#include <linux/types.h>
#include <sys/types.h>
#include <unique_fd.h>
#include "debug_logger.h"
#include "mem_map_item.h"
#include "perf_record_format.h"
#include "utilities.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
static constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
enum perf_event_hiperf_ext_type {
PERF_RECORD_HIPERF_CALLSTACK = UINT32_MAX / 2,
};
struct CallFrame {
uint64_t ip_ = 0;
uint64_t sp_ = 0;
uint64_t vaddrInFile_ = 0; // in symbol file vaddr
int32_t symbolIndex_ = -1; // symbols index , should update after sort
std::string_view symbolName_;
std::string_view filePath_; // lib path , elf path
CallFrame(uint64_t ip, uint64_t sp = 0) : ip_(ip), sp_(sp) {}
// this is for ut test
CallFrame(uint64_t ip, uint64_t vaddrInFile, const char *name, const char *filePath)
: ip_(ip), vaddrInFile_(vaddrInFile), symbolName_(name), filePath_(filePath)
{
}
bool operator==(const CallFrame &b) const
{
return (ip_ == b.ip_) && (sp_ == b.sp_);
}
bool operator!=(const CallFrame &b) const
{
return (ip_ != b.ip_) || (sp_ != b.sp_);
}
std::string ToString() const
{
return StringPrintf("ip: 0x%016llx sp: 0x%016llx", ip_, sp_);
}
std::string ToSymbolString() const
{
std::string output;
if (vaddrInFile_ != 0) {
output = StringPrintf("va: 0x%016llx(%llx) ", vaddrInFile_, ip_);
} else {
output = StringPrintf("ip: 0x%016llx ", ip_);
}
output.append(": ");
output.append(symbolName_);
output.append("@");
output.append(filePath_);
if (symbolIndex_ != -1) {
output.append(":");
output.append(std::to_string(symbolIndex_));
}
return output;
}
};
struct AttrWithId {
perf_event_attr attr;
std::vector<uint64_t> ids;
std::string name; // will be empty in GetAttrSection
};
class PerfEventRecord {
public:
PerfEventRecord(const PerfEventRecord &) = delete;
PerfEventRecord &operator=(const PerfEventRecord &) = delete;
struct perf_event_header header;
const std::string name_ {};
PerfEventRecord(perf_event_type type, bool in_kernel, const std::string &name);
PerfEventRecord(perf_event_hiperf_ext_type type, const std::string &name);
PerfEventRecord(uint8_t *p, const std::string &name);
virtual ~PerfEventRecord() {};
virtual size_t GetSize() const
{
return header.size;
};
size_t GetHeaderSize() const
{
return sizeof(header);
};
void GetHeaderBinary(std::vector<uint8_t> &buf) const;
uint32_t GetType() const
{
return header.type;
};
uint16_t GetMisc() const
{
return header.misc;
};
bool inKernel()
{
return header.misc & PERF_RECORD_MISC_KERNEL;
}
bool inUser()
{
return header.misc & PERF_RECORD_MISC_USER;
}
const std::string &GetName() const
{
return name_;
};
// to support --exclude-hiperf, return sample_id.pid to filter record,
virtual pid_t GetPid() const
{
return 0;
};
virtual bool GetBinary(std::vector<uint8_t> &buf) const = 0;
void Dump(int indent = 0) const;
virtual void DumpData(int indent) const = 0;
virtual void DumpLog(const std::string &prefix) const;
};
// define convert from linux/perf_event.h
// description from https://man7.org/linux/man-pages/man2/perf_event_open.2.html
constexpr __u64 SAMPLE_ID = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER;
constexpr __u64 SAMPLE_TYPE = PERF_SAMPLE_IP | SAMPLE_ID | PERF_SAMPLE_PERIOD;
constexpr __u32 MIN_SAMPLE_STACK_SIZE = 8;
constexpr __u32 MAX_SAMPLE_STACK_SIZE = 65528;
class PerfRecordMmap : public PerfEventRecord {
public:
PerfRecordMmapData data_;
PerfRecordMmap(uint8_t *p);
PerfRecordMmap(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff,
const std::string &filename);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
virtual void DumpLog(const std::string &prefix) const override;
};
class PerfRecordMmap2 : public PerfEventRecord {
public:
PerfRecordMmap2Data data_;
PerfRecordMmap2(uint8_t *p);
PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff, u32 maj, u32 min,
u64 ino, u32 prot, u32 flags, const std::string &filename);
PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, const MemMapItem &item);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
virtual void DumpLog(const std::string &prefix) const override;
};
class PerfRecordLost : public PerfEventRecord {
public:
PerfRecordLostData data_;
PerfRecordLost(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
// only for UT
PerfRecordLost(bool inKernel, u64 id, u64 lost)
: PerfEventRecord(PERF_RECORD_LOST, inKernel, "lost")
{
data_.id = id;
data_.lost = lost;
header.size = sizeof(header) + sizeof(data_);
}
};
class PerfRecordComm : public PerfEventRecord {
public:
PerfRecordCommData data_;
PerfRecordComm(uint8_t *p);
PerfRecordComm(bool inKernel, u32 pid, u32 tid, const std::string &comm);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
void DumpLog(const std::string &prefix) const override;
};
class PerfRecordSample : public PerfEventRecord {
public:
PerfRecordSampleData data_ = {};
uint64_t sampleType_ = SAMPLE_TYPE;
// extend
// hold the new ips memory (after unwind)
// used for data_.ips replace (ReplaceWithCallStack)
std::vector<u64> ips_;
std::vector<CallFrame> callFrames_;
// referenced input(p) in PerfRecordSample, require caller keep input(p) together
PerfRecordSample(uint8_t *p, const perf_event_attr &attr);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent = 0) const override;
virtual void DumpLog(const std::string &prefix) const override;
void ReplaceWithCallStack();
pid_t GetPid() const override;
// only for UT
PerfRecordSample(bool inKernel, u32 pid, u32 tid, u64 period = 0, u64 time = 0, u64 id = 0)
: PerfEventRecord(PERF_RECORD_SAMPLE, inKernel, "sample")
{
data_.pid = pid;
data_.tid = tid;
data_.period = period;
data_.time = time;
data_.id = 0;
header.size = sizeof(header) + sizeof(data_);
};
};
class PerfRecordExit : public PerfEventRecord {
public:
PerfRecordExitData data_;
PerfRecordExit(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
class PerfRecordThrottle : public PerfEventRecord {
public:
PerfRecordThrottleData data_;
PerfRecordThrottle(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
class PerfRecordUnthrottle : public PerfEventRecord {
public:
PerfRecordThrottleData data_;
PerfRecordUnthrottle(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
class PerfRecordFork : public PerfEventRecord {
public:
PerfRecordForkData data_;
PerfRecordFork(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
/*
This record indicates a read event.
*/
class PerfRecordRead : public PerfEventRecord {
public:
PerfRecordReadData data_;
PerfRecordRead(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
/*
This record reports that new data is available in the
separate AUX buffer region.
aux_offset
offset in the AUX mmap region where the new
data begins.
aux_size
size of the data made available.
flags describes the AUX update.
PERF_AUX_FLAG_TRUNCATED
if set, then the data returned was
truncated to fit the available buffer
size.
PERF_AUX_FLAG_OVERWRITE
if set, then the data returned has
overwritten previous data.
*/
class PerfRecordAux : public PerfEventRecord {
public:
PerfRecordAuxData data_;
PerfRecordAux(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
/*
This record indicates which process has initiated an
instruction trace event, allowing tools to properly
correlate the instruction addresses in the AUX buffer
with the proper executable.
pid process ID of the thread starting an
instruction trace.
tid thread ID of the thread starting an instruction
trace.
*/
class PerfRecordItraceStart : public PerfEventRecord {
public:
PerfRecordItraceStartData data_;
PerfRecordItraceStart(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
/*
When using hardware sampling (such as Intel PEBS) this
record indicates some number of samples that may have
been lost.
*/
class PerfRecordLostSamples : public PerfEventRecord {
public:
PerfRecordLostSamplesData data_;
PerfRecordLostSamples(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
/*
This record indicates a context switch has happened.
The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field
indicates whether it was a context switch into or away
from the current process.
*/
class PerfRecordSwitch : public PerfEventRecord {
public:
PerfRecordSwitchData data_;
PerfRecordSwitch(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData([[maybe_unused]] int indent) const override {};
};
/*
As with PERF_RECORD_SWITCH this record indicates a
context switch has happened, but it only occurs when
sampling in CPU-wide mode and provides additional
information on the process being switched to/from.
The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field
indicates whether it was a context switch into or away
from the current process.
next_prev_pid
The process ID of the previous (if switching
in) or next (if switching out) process on the
CPU.
next_prev_tid
The thread ID of the previous (if switching in)
or next (if switching out) thread on the CPU.
*/
class PerfRecordSwitchCpuWide : public PerfEventRecord {
public:
PerfRecordSwitchCpuWideData data_;
PerfRecordSwitchCpuWide(uint8_t *p);
bool GetBinary(std::vector<uint8_t> &buf) const override;
virtual void DumpData(int indent) const override;
};
std::unique_ptr<PerfEventRecord> GetPerfEventRecord(const int type, uint8_t *data,
const perf_event_attr &attr);
template<typename T>
void PushToBinary(bool condition, uint8_t *&p, const T &v);
template<typename T1, typename T2>
void PushToBinary2(bool condition, uint8_t *&p, const T1 &v1, const T2 &v2);
template<typename T>
void PopFromBinary(bool condition, uint8_t *&p, T &v);
template<typename T1, typename T2>
void PopFromBinary2(bool condition, uint8_t *&p, T1 &v1, T2 &v2);
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

463
include/perf_events.h Executable file
View File

@ -0,0 +1,463 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_PERF_EVENTS_H
#define HIPERF_PERF_EVENTS_H
#include <atomic>
#include <cassert>
#include <chrono>
#include <cinttypes>
#include <condition_variable>
#include <deque>
#include <map>
#include <memory>
#include <string>
#include <thread>
#include <variant>
#include <vector>
#if !is_mingw
#include <poll.h>
#endif
#include <linux/perf_event.h>
#include <sys/types.h>
#include <unique_fd.h>
#include "debug_logger.h"
#include "perf_event_record.h"
#include "ring_buffer.h"
#include "tracked_command.h"
#include "utilities.h"
#include "virtual_runtime.h"
// this for some performance debug
#define HIDEBUG_SKIP_CALLBACK 0
namespace OHOS {
namespace Developtools {
namespace HiPerf {
using ConfigTable = std::map<__u64, const std::string>;
using SharedConfigTable = std::unique_ptr<ConfigTable>;
static const std::string PERF_EVENT_PARANOID = "/proc/sys/kernel/perf_event_paranoid";
static const std::string PERF_DISABLE_PARAM = "security.perf_harden";
// define convert from linux/perf_event.h
// description from https://man7.org/linux/man-pages/man2/perf_event_open.2.html
static const ConfigTable PERF_HW_CONFIGS = {
{PERF_COUNT_HW_CPU_CYCLES, "hw-cpu-cycles"},
{PERF_COUNT_HW_INSTRUCTIONS, "hw-instructions"},
{PERF_COUNT_HW_CACHE_REFERENCES, "hw-cache-references"},
{PERF_COUNT_HW_CACHE_MISSES, "hw-cache-misses"},
{PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "hw-branch-instructions"},
{PERF_COUNT_HW_BRANCH_MISSES, "hw-branch-misses"},
{PERF_COUNT_HW_BUS_CYCLES, "hw-bus-cycles"},
{PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, "hw-stalled-cycles-backend"},
{PERF_COUNT_HW_STALLED_CYCLES_BACKEND, "hw-stalled-cycles-frontend"},
{PERF_COUNT_HW_REF_CPU_CYCLES, "hw-ref-cpu-cycles"},
};
static const ConfigTable PERF_HW_CACHE_CONFIGS = {
{PERF_COUNT_HW_CACHE_L1D, "hw-cache-l1d"}, {PERF_COUNT_HW_CACHE_L1I, "hw-cache-l1i"},
{PERF_COUNT_HW_CACHE_LL, "hw-cache-ll"}, {PERF_COUNT_HW_CACHE_DTLB, "hw-cache-dtlb"},
{PERF_COUNT_HW_CACHE_ITLB, "hw-cache-itlb"}, {PERF_COUNT_HW_CACHE_BPU, "hw-cache-bpu"},
{PERF_COUNT_HW_CACHE_NODE, "hw-cache-node"},
};
static const ConfigTable PERF_HW_CACHE_OP_CONFIGS = {
{PERF_COUNT_HW_CACHE_OP_READ, "hw-cache-op-read"},
{PERF_COUNT_HW_CACHE_OP_WRITE, "hw-cache-op-write"},
{PERF_COUNT_HW_CACHE_OP_PREFETCH, "hw-cache-op-prefetch"},
};
static const ConfigTable PERF_HW_CACHE_OP_RESULT_CONFIGS = {
{PERF_COUNT_HW_CACHE_RESULT_ACCESS, "hw-cache-result-access"},
{PERF_COUNT_HW_CACHE_RESULT_MISS, "hw-cache-result-miss"},
};
static const ConfigTable PERF_SW_CONFIGS = {
{PERF_COUNT_SW_CPU_CLOCK, "sw-cpu-clock"},
{PERF_COUNT_SW_TASK_CLOCK, "sw-task-clock"},
{PERF_COUNT_SW_PAGE_FAULTS, "sw-page-faults"},
{PERF_COUNT_SW_CONTEXT_SWITCHES, "sw-context-switches"},
{PERF_COUNT_SW_CPU_MIGRATIONS, "sw-cpu-migrations"},
{PERF_COUNT_SW_PAGE_FAULTS_MIN, "sw-page-faults-min"},
{PERF_COUNT_SW_PAGE_FAULTS_MAJ, "sw-page-faults-maj"},
{PERF_COUNT_SW_ALIGNMENT_FAULTS, "sw-alignment-faults"},
{PERF_COUNT_SW_EMULATION_FAULTS, "sw-emulation-faults"},
{PERF_COUNT_SW_DUMMY, "sw-dummy"},
{PERF_COUNT_SW_BPF_OUTPUT, "sw-bpf-output"},
};
static const ConfigTable PERF_RAW_CONFIGS = {
#include "arm_raw_event_type_table.h"
};
static ConfigTable PERF_TRACEPOINT_CONFIGS = {
};
static const std::map<perf_type_id, std::string> PERF_TYPES = {
{PERF_TYPE_HARDWARE, "hardware"},
{PERF_TYPE_SOFTWARE, "software"},
{PERF_TYPE_TRACEPOINT, "tracepoint"},
{PERF_TYPE_HW_CACHE, "hardware cache"},
{PERF_TYPE_RAW, "raw"},
};
static std::map<perf_type_id, ConfigTable> TYPE_CONFIGS = {
{PERF_TYPE_HARDWARE, (PERF_HW_CONFIGS)}, {PERF_TYPE_SOFTWARE, (PERF_SW_CONFIGS)},
{PERF_TYPE_HW_CACHE, (PERF_HW_CACHE_CONFIGS)}, {PERF_TYPE_RAW, (PERF_RAW_CONFIGS)},
{PERF_TYPE_TRACEPOINT, (PERF_TRACEPOINT_CONFIGS)},
};
// default config
static const std::vector<__u64> DEFAULT_HW_CONFIGS = {
PERF_COUNT_HW_CPU_CYCLES,
#if defined(__aarch64__)
PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
#endif
PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_MISSES,
};
static const std::vector<__u64> DEFAULT_SW_CONFIGS = {
PERF_COUNT_SW_TASK_CLOCK,
PERF_COUNT_SW_CONTEXT_SWITCHES,
PERF_COUNT_SW_PAGE_FAULTS,
};
static const std::map<perf_type_id, std::vector<__u64>> DEFAULT_TYPE_CONFIGS = {
{PERF_TYPE_HARDWARE, DEFAULT_HW_CONFIGS},
{PERF_TYPE_SOFTWARE, DEFAULT_SW_CONFIGS},
};
struct read_format_event {
__u64 value; /* The value of the event */
__u64 id; /* if PERF_FORMAT_ID */
};
struct read_format_group {
__u64 nr; /* The number of events */
__u64 time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
__u64 time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
read_format_event events[1];
};
struct read_format_no_group {
__u64 value; /* The value of the event */
__u64 time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
__u64 time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
__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;
static constexpr uint64_t DEFAULT_SAMPLE_PERIOD = 1;
static constexpr uint64_t DEFAULT_TIMEOUT = 10 * 1000;
static constexpr size_t MIN_BUFFER_SIZE = 64 * 1024 * 1024;
static constexpr size_t MAX_BUFFER_SIZE = 256 * 1024 * 1024;
static constexpr size_t BUFFER_LOW_LEVEL = 10 * 1024 * 1024;
static constexpr size_t BUFFER_CRITICAL_LEVEL = 5 * 1024 * 1024;
PerfEvents();
~PerfEvents();
bool AddEvents(const std::vector<std::string> &eventStrings, bool group = false);
bool PrepareTracking(void);
bool StartTracking(bool immediately = true);
bool StopTracking(void);
bool PauseTracking(void);
bool ResumeTracking(void);
/* call sequence
1. setXXX
2. AddEvents()
3. PrepareTracking
4. StartTracking (blocking...)
*/
bool EnableTracking();
bool IsTrackRunning();
void SetSystemTarget(bool);
void SetCpu(const std::vector<pid_t> cpus); // cpu id must be [0~N]
void SetPid(const std::vector<pid_t> pids); // tis is same as pid in kernel
void SetTimeOut(float timeOut);
void SetTimeReport(int);
void SetVerboseReport(bool);
inline void SetTrackedCommand(const std::vector<std::string> &trackedCommand)
{
if (!trackedCommand.empty()) {
trackedCommand_ = TrackedCommand::CreateInstance(trackedCommand);
}
}
void SetSampleFrequency(unsigned int frequency);
void SetSamplePeriod(unsigned int period);
enum SampleStackType {
NONE,
FP,
DWARF,
};
void SetSampleStackType(SampleStackType type);
void SetDwarfSampleStackSize(uint32_t stackSize);
void SetMmapPages(size_t mmapPages);
std::vector<AttrWithId> GetAttrWithId() const;
void SetInherit(bool inherit)
{
inherit_ = inherit;
};
void SetClockId(int clockId)
{
clockId_ = clockId;
};
bool SetBranchSampleType(uint64_t value);
bool AddDefaultEvent(perf_type_id type);
std::map<__u64, std::string> GetSupportEvents(perf_type_id type);
struct CountEvent {
bool userOnly = false;
bool kernelOnly = false;
__u64 eventCount = 0;
__u64 time_enabled = 0;
__u64 time_running = 0;
__u64 id = 0;
double used_cpus = 0;
};
using StatCallBack =
std::function<void(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &)>;
using RecordCallBack = std::function<bool(std::unique_ptr<PerfEventRecord>)>;
void SetStatCallBack(StatCallBack reportCallBack);
void SetRecordCallBack(RecordCallBack recordCallBack);
void GetLostSamples(size_t &lostSamples, size_t &lostNonSamples)
{
lostSamples = lostSamples_;
lostNonSamples = lostNonSamples_;
}
// review: remvoe this funcion.
static const std::string GetStaticConfigName(perf_type_id type_id, __u64 config_id)
{
auto typeConfigs = TYPE_CONFIGS.find(type_id);
if (typeConfigs != TYPE_CONFIGS.end()) {
auto configs = typeConfigs->second;
auto config = configs.find(config_id);
if (config != configs.end()) {
return config->second;
} else {
HLOGW("config not found for %u:%lld in %zu:%zu", type_id, config_id,
TYPE_CONFIGS.size(), configs.size());
// dump all config size
for (auto types : TYPE_CONFIGS) {
HLOGV("type id %d %zu", types.first, types.second.size());
}
}
} else {
HLOGW("type not found for %d in %zu", type_id, TYPE_CONFIGS.size());
}
return "<not found>";
};
const std::string GetTraceConfigName(__u64 config_id)
{
auto config = traceConfigTable.find(config_id);
if (config != traceConfigTable.end()) {
return config->second;
} else {
HLOGW("config not found for %lld in traceConfigTable.", config_id);
}
return "<not found>";
};
static const std::string GetTypeName(perf_type_id type_id);
bool CheckPermissions(PerfEventParanoid request = KERNEL_USER_CPU);
bool ParseEventName(const std::string &nameStr, std::string &name, bool &excludeUser,
bool &excludeKernel, bool &isTracePoint);
// mmap one fd for each cpu
struct MmapFd {
int fd;
perf_event_mmap_page *mmapPage = nullptr;
uint8_t *buf = nullptr;
size_t bufSize = 0;
// for read and sort
size_t dataSize = 0;
perf_event_header header;
uint64_t timestamp = 0;
const perf_event_attr *attr = nullptr;
size_t posCallChain = 0;
};
private:
size_t recordEventCount_ = 0; // only for debug time
#ifdef HIPERF_DEBUG_TIME
std::chrono::microseconds recordCallBackTime_ = std::chrono::microseconds::zero();
std::chrono::microseconds recordWaitDataTime_ = std::chrono::microseconds::zero();
std::chrono::microseconds recordSleepTime_ = std::chrono::microseconds::zero();
std::chrono::microseconds recordKernelReadTime_ = std::chrono::microseconds::zero();
#endif
size_t lostSamples_ = 0;
size_t lostNonSamples_ = 0;
std::unique_ptr<RingBuffer> recordBuf_ {nullptr};
std::mutex mtxRrecordBuf_;
std::condition_variable cvRecordBuf_;
std::thread readRecordBufThread_;
std::atomic_bool readRecordThreadRunning_ = false;
bool startedTracking_ = false;
bool isLowPriorityThread_ = false;
void RecordLoop();
void StatLoop();
bool IsRecordInMmap();
void ReadRecordsFromMmaps();
bool GetRecordFromMmap(MmapFd &mmap);
void GetRecordFieldFromMmap(MmapFd &mmap, void *dest, size_t pos, size_t size);
void MoveRecordToBuf(MmapFd &mmap);
size_t GetCallChainPosInSampleRecord(const perf_event_attr &attr);
size_t GetStackSizePosInSampleRecord(MmapFd &mmap);
bool CutStackAndMove(MmapFd &mmap);
void ReadRecordFromBuf();
size_t CalcBufferSize();
bool PrepareRecordThread();
void WaitRecordThread();
bool HaveTargetsExit(const std::chrono::steady_clock::time_point &startTime);
void ExitReadRecordBufThread();
bool CheckOhosPermissions();
static PerfEventParanoid perfEventParanoid_;
bool inherit_ = false;
std::vector<pid_t> pids_;
std::vector<pid_t> cpus_;
std::vector<OHOS::UniqueFd> groups_;
std::chrono::milliseconds timeOut_; // milliseconds
std::chrono::milliseconds timeReport_; // means same as timeOut
bool verboseReport_ = false;
bool prepared_ = false;
ConfigTable traceConfigTable;
unsigned int samplePeriod_ = 0;
unsigned int sampleFreq_ = 0;
struct FdItem {
OHOS::UniqueFd fd;
int cpu;
pid_t pid;
__u64 eventCount;
mutable uint64_t perf_id_ = 0;
uint64_t GetPrefId() const
{
if (perf_id_ == 0) {
read_format_no_group readNoGroupValue;
if (read(fd, &readNoGroupValue, sizeof(readNoGroupValue)) > 0) {
perf_id_ = readNoGroupValue.id;
} else {
HLOGW("read failed with fd %d", fd.Get());
}
}
return perf_id_;
}
};
struct EventItem {
std::string typeName;
std::string configName;
perf_event_attr attr = {};
std::vector<FdItem> fdItems;
};
struct EventGroupItem {
std::vector<EventItem> eventItems;
};
std::vector<EventGroupItem> eventGroupItem_;
std::map<int, MmapFd> cpuMmap_;
std::vector<MmapFd *> MmapRecordHeap_;
#if !is_mingw
std::vector<struct pollfd> pollFds_;
#endif
const int pollTimeOut_ = 100; // ms
size_t pageSize_ = 4096;
bool systemTarget_ = false;
bool excludeHiperf_ = false;
pid_t selfPid_ = -1;
unsigned int mmapPages_ = 0;
int clockId_ = -1;
uint64_t branchSampleType_ = 0;
SampleStackType sampleStackType_ = SampleStackType::NONE;
uint32_t dwarfSampleStackSize_ = MAX_SAMPLE_STACK_SIZE;
// read records from the ring buffer singlton
void ReadRecordFromBuffer();
void ReadRecordFromBufferThread();
std::unique_ptr<TrackedCommand> trackedCommand_ = {};
StatCallBack reportCallBack_;
RecordCallBack recordCallBack_;
void LoadTracepointEventTypesFromSystem();
bool PerfEventsEnable(bool);
bool AddEvent(perf_type_id type, __u64 config, bool excludeUser = false,
bool excludeKernel = false, bool followGroup = false);
bool AddEvent(const std::string &eventString, bool followGroup = false);
bool IsEventSupport(perf_type_id type, __u64 config);
bool IsEventAttrSupport(perf_event_attr &attr);
std::chrono::time_point<std::chrono::steady_clock> trackingStartTime_;
std::chrono::time_point<std::chrono::steady_clock> trackingEndTime_;
std::chrono::time_point<std::chrono::steady_clock> readingStartTime_;
std::map<std::string, std::unique_ptr<CountEvent>> countEvents_;
void PutAllCpus();
bool PrepareFdEvents();
bool CreateFdEvents();
bool StatReport(const __u64 &durationInSec);
bool CreateMmap(const FdItem &item, const perf_event_attr &attr);
const perf_event_attr *GetDefaultAttr()
{
HLOG_ASSERT(eventGroupItem_.size() > 0);
HLOG_ASSERT(eventGroupItem_[0].eventItems.size() > 0);
return &(eventGroupItem_.at(0).eventItems.at(0).attr);
};
OHOS::UniqueFd Open(perf_event_attr &attr, pid_t pid = 0, int cpu = -1, int group_fd = -1,
unsigned long flags = 0);
std::unique_ptr<perf_event_attr> CreateDefaultAttr(perf_type_id type, __u64 config);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

259
include/perf_file_format.h Executable file
View File

@ -0,0 +1,259 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_PERF_FILE_FORMAT_H
#define HIPERF_PERF_FILE_FORMAT_H
#include <string>
#include <vector>
#include "perf_event_record.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
enum class FEATURE {
RESERVED = 0, /* always cleared */
FIRST_FEATURE = 1,
TRACING_DATA = 1,
BUILD_ID, // build_id_event
HOSTNAME, // A perf_header_string with the hostname where the data was collected (uname -n)
OSRELEASE, // A perf_header_string with the os release where the data was collected (uname -r)
VERSION, // A perf_header_string with the perf user tool version where the data was collected.
// This is the same as the version of the source tree the perf tool was built from.
ARCH, // A perf_header_string with the CPU architecture (uname -m)
NRCPUS, // A structure defining the number of CPUs.
CPUDESC, // A perf_header_string with description of the CPU. On x86 this is the model name
// in /proc/cpuinfo
CPUID, // A perf_header_string with the exact CPU type. On x86 this is
// vendor,family,model,stepping. For example: GenuineIntel,6,69,1
TOTAL_MEM, // An uint64_t with the total memory in kilobytes.
CMDLINE, // A perf_header_string_list with the perf arg-vector used to collect the data.
EVENT_DESC, // Another description of the perf_event_attrs
CPU_TOPOLOGY, //
NUMA_TOPOLOGY, // A list of NUMA node descriptions
BRANCH_STACK, // Not implemented in perf.
PMU_MAPPINGS, // A list of PMU structures, defining the different PMUs supported by perf.
GROUP_DESC, // Description of counter groups ({...} in perf syntax)
AUXTRACE, // Define additional auxtrace areas in the perf.data. auxtrace is used to store
// undecoded hardware tracing information, such as Intel Processor Trace data.
STAT,
CACHE,
SAMPLE_TIME,
MEM_TOPOLOGY,
LAST_FEATURE,
HIPERF_FIRST_FEATURE = 192,
HIPERF_FILES_SYMBOL = HIPERF_FIRST_FEATURE,
HIPERF_WORKLOAD_CMD,
HIPERF_RECORD_TIME,
HIPERF_CPU_OFF,
HIPERF_LAST_FEATURE = HIPERF_CPU_OFF,
FEATURE_MAX_BITS = 256,
};
const static std::vector<FEATURE> FeatureStrings = {
FEATURE::HOSTNAME,
FEATURE::OSRELEASE,
FEATURE::VERSION,
FEATURE::ARCH,
FEATURE::CPUDESC,
FEATURE::CPUID,
FEATURE::CMDLINE,
FEATURE::HIPERF_WORKLOAD_CMD,
FEATURE::HIPERF_RECORD_TIME,
};
struct perf_file_section {
uint64_t offset;
uint64_t size;
};
struct perf_file_attr {
perf_event_attr attr;
perf_file_section ids;
};
struct perf_header_string {
uint32_t len;
char string[0]; /* zero terminated */
};
constexpr char PERF_MAGIC[] = "PERFILE2";
constexpr int BITS_IN_BYTE = 8;
constexpr int NUM_FEATURES_FILE_HEADER = 256;
struct perf_file_header {
char magic[8] = {'P', 'E', 'R', 'F', 'I', 'L', 'E', '2'};
uint64_t size = sizeof(perf_file_header);
uint64_t attrSize = sizeof(perf_file_attr);
perf_file_section attrs;
perf_file_section data;
perf_file_section eventTypes;
uint8_t features[NUM_FEATURES_FILE_HEADER / BITS_IN_BYTE] = {0};
};
static const std::vector<std::string> extFeatureNames = {
"hiperf_files_symbol",
"hiperf_workloader_cmd",
"hiperf_record_time",
"hiperf_cpu_off",
};
static const std::vector<std::string> featureNames = {
"unknown_feature", "tracing_data", "build_id", "hostname", "osrelease",
"version", "arch", "nrcpus", "cpudesc", "cpuid",
"total_mem", "cmdline", "event_desc", "cpu_topology", "numa_topology",
"branch_stack", "pmu_mappings", "group_desc", "auxtrace", "stat",
"cache", "sample_time", "mem_topology", "last_feature",
};
class PerfFileSection {
public:
struct perf_file_section header;
const FEATURE featureId_;
virtual bool GetBinary(char *buf, size_t size) = 0;
virtual size_t GetSize() = 0;
virtual ~PerfFileSection() {};
explicit PerfFileSection(const FEATURE featureId) : featureId_(featureId) {}
static std::string GetFeatureName(FEATURE featureId);
protected:
const char *rBuffer_ = nullptr;
char *wBuffer_ = nullptr;
size_t maxSize_ = 0;
size_t offset_ = 0;
// for read
void Init(const char *buffer, size_t maxSize);
// for write
void Init(char *buffer, size_t maxSize);
bool Write(uint32_t u32);
bool Write(uint64_t u64);
bool Write(const std::string &str);
bool Write(const char *buf, size_t size);
bool Write(const char *buf, size_t size, size_t max);
bool Read(uint32_t &value);
bool Read(uint64_t &value);
bool Read(std::string &value);
bool Read(char *buf, size_t size);
void Skip(size_t size);
uint32_t SizeOf(std::string &string);
};
class PerfFileSectionString : public PerfFileSection {
std::string stdString_;
public:
// convert buff to PerfFileSectionString, used to read file
// if the data in buf is incorrect, ......
PerfFileSectionString(FEATURE id, const char *buf, size_t size);
PerfFileSectionString(FEATURE id, const std::string &charString);
bool GetBinary(char *buf, size_t size);
size_t GetSize();
const std::string toString() const;
};
// ref struct
struct SymbolStruct {
uint64_t vaddr_ = 0;
uint32_t len_ = 0;
std::string symbolName_ = EMPTY_STRING;
SymbolStruct() {};
SymbolStruct(uint64_t vaddr, uint32_t len, const std::string &symbolName)
: vaddr_(vaddr), len_(len), symbolName_(symbolName)
{
}
};
struct SymbolFileStruct {
std::string filePath_ = EMPTY_STRING;
uint32_t symbolType_;
uint64_t textExecVaddr_;
uint64_t textExecVaddrFileOffset_;
std::string buildId_;
std::vector<SymbolStruct> symbolStructs_;
};
class PerfFileSectionSymbolsFiles : public PerfFileSection {
public:
std::vector<SymbolFileStruct> symbolFileStructs_;
size_t GetSize();
PerfFileSectionSymbolsFiles(FEATURE id, const std::vector<SymbolFileStruct> &symbolFileStructs)
: PerfFileSection(id), symbolFileStructs_(symbolFileStructs)
{
}
// if the data in buf is incorrect, ......
PerfFileSectionSymbolsFiles(FEATURE id, const char *buf, size_t size);
bool GetBinary(char *buf, size_t size);
private:
// issue from fuzz test
const size_t MAX_SYMBOLS_FILE_NUMBER = 100;
const size_t MAX_SYMBOLS_NUMBER = 1000;
};
// NRCPUS: A structure defining the number of CPUs.
class PerfFileSectionNrCpus : public PerfFileSection {
uint32_t nrCpusAvailable_; /* CPUs not yet onlined */
uint32_t nrCpusOnline_;
public:
PerfFileSectionNrCpus(FEATURE id, const char *buf, size_t size);
PerfFileSectionNrCpus(FEATURE id, uint32_t nrCpusAvailable, uint32_t nrCpusOnline);
bool GetBinary(char *buf, size_t size);
size_t GetSize();
void GetValue(uint32_t &nrCpusAvailable, uint32_t &nrCpusOnline) const;
};
class PerfFileSectionU64 : public PerfFileSection {
uint64_t value_;
public:
PerfFileSectionU64(FEATURE id, const char *buf, size_t size);
PerfFileSectionU64(FEATURE id, uint64_t v);
bool GetBinary(char *buf, size_t size);
size_t GetSize();
void GetValue(uint64_t &v) const;
};
struct AttrWithId;
class PerfFileSectionEventDesc : public PerfFileSection {
public:
std::vector<AttrWithId> eventDesces_;
PerfFileSectionEventDesc(FEATURE id, const char *buf, size_t size);
PerfFileSectionEventDesc(FEATURE id, const std::vector<AttrWithId> &eventDesces);
bool GetBinary(char *buf, size_t size);
size_t GetSize();
void GetValue(std::vector<AttrWithId> &eventDesces) const;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

102
include/perf_file_reader.h Executable file
View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_FILE_READER
#define HIPERF_FILE_READER
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
#include "perf_event_record.h"
#include "perf_file_format.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
using ProcessRecordCB = const std::function<bool(std::unique_ptr<PerfEventRecord> record)>;
// read record from data file, like perf.data.
// format of file follow
// tools/perf/Documentation/perf.data-file-format.txt
class PerfFileReader {
public:
virtual ~PerfFileReader();
static std::unique_ptr<PerfFileReader> Instance(const std::string &fileName);
const perf_file_header &GetHeader() const;
std::vector<AttrWithId> GetAttrSection() const;
// read data section, construct record, call callback for each record
bool ReadDataSection(ProcessRecordCB &callback);
bool ReadFeatureSection();
const std::vector<FEATURE> &GetFeatures() const;
const std::vector<std::unique_ptr<PerfFileSection>> &GetFeatureSections() const;
const PerfFileSection *GetFeatureSection(FEATURE feature) const;
explicit PerfFileReader(const std::string &fileName, FILE *fp);
const std::string GetFeatureString(const FEATURE feature) const;
bool IsFeatrureStringSection(const FEATURE featureId) const
{
return find(FeatureStrings.begin(), FeatureStrings.end(), featureId) !=
FeatureStrings.end();
}
// fuzz user this
protected:
virtual bool Read(void *buf, size_t len);
virtual bool Read(char *buf, uint64_t offset, size_t len);
FILE *fp_ = nullptr;
bool ReadFileHeader();
bool ReadAttrSection();
private:
bool ReadRecord(ProcessRecordCB &callback);
bool IsValidDataFile();
bool IsGzipFile();
// file header must be read first
bool ReadIdsForAttr(const perf_file_attr &attr, std::vector<uint64_t> *ids);
const perf_event_attr *GetDefaultAttr();
const std::string fileName_;
uint64_t dataSectionSize_;
bool compressData_ = false;
perf_file_header header_;
std::vector<perf_file_attr> vecAttr_;
std::vector<std::vector<uint64_t>> vecAttrIds_;
std::unordered_map<uint64_t, size_t> mapId2Attr_;
uint64_t featureSectionOffset_;
std::vector<FEATURE> features_;
std::vector<std::unique_ptr<PerfFileSection>> perfFileSections_;
size_t fileSize_ = 0;
#ifdef HIPERF_DEBUG_TIME
std::chrono::microseconds readRecordTime_ = std::chrono::microseconds::zero();
std::chrono::microseconds readCallbackTime_ = std::chrono::microseconds::zero();
#endif
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

86
include/perf_file_writer.h Executable file
View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_PERF_FILE_WRITER_H
#define HIPERF_PERF_FILE_WRITER_H
#include <functional>
#include <unordered_map>
#include <vector>
#include "perf_event_record.h"
#include "perf_file_format.h"
#include "symbols_file.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
constexpr const int WRITER_BUFFER_SIZE = 4 * 1024 * 1024;
// write record to data file, like perf.data.
// format of file follow
// tools/perf/Documentation/perf.data-file-format.txt
class PerfFileWriter {
public:
PerfFileWriter() {};
~PerfFileWriter();
bool Open(const std::string &fileName, bool compressData = false);
// WriteAttrAndId() must be called before WriteRecord()
bool WriteAttrAndId(const std::vector<AttrWithId> &attrIds);
bool WriteRecord(const PerfEventRecord &record);
bool AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable, uint32_t nrCpusOnline);
bool AddEventDescFeature(FEATURE feature, const std::vector<AttrWithId> &eventDesces);
bool AddStringFeature(FEATURE feature, std::string string);
bool AddU64Feature(FEATURE feature, uint64_t v);
bool AddBoolFeature(FEATURE feature);
bool AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> &);
// close file
bool Close();
uint64_t GetDataSize() const;
uint GetRecordCount() const;
std::chrono::microseconds writeTimes_ = std::chrono::microseconds::zero();
using ProcessRecordCB = const std::function<bool(std::unique_ptr<PerfEventRecord> record)>;
bool ReadDataSection(ProcessRecordCB &callback);
bool ReadRecords(ProcessRecordCB &callback);
bool Read(void *buf, size_t len);
private:
std::string fileBuffer_;
bool GetFilePos(uint64_t &pos) const;
bool Write(const void *buf, size_t len);
bool WriteHeader();
bool WriteFeatureData();
std::string fileName_;
FILE *fp_ = nullptr;
perf_file_header header_;
perf_file_section attrSection_;
perf_file_section dataSection_;
std::vector<std::unique_ptr<PerfFileSection>> featureSections_;
perf_event_attr defaultEventAttr_;
uint recordCount_ = 0;
bool compressData_ = false;
bool isWritingRecord = false;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

360
include/perf_record_format.h Executable file
View File

@ -0,0 +1,360 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_PERF_RECORD_FORMAT_H
#define HIPERF_PERF_RECORD_FORMAT_H
#include <linux/types.h>
#include <string>
#include "utilities.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
// description from https://man7.org/linux/man-pages/man2/perf_event_open.2.html
#define SAMPLE_ID_ALL 0
struct sample_id {
u32 pid;
u32 tid; /* if PERF_SAMPLE_TID set */
u64 time; /* if PERF_SAMPLE_TIME set */
u64 id; /* if PERF_SAMPLE_ID set */
u64 stream_id; /* if PERF_SAMPLE_STREAM_ID set */
u32 cpu, res; /* if PERF_SAMPLE_CPU set */
u64 id2; /* if PERF_SAMPLE_IDENTIFIER set */
};
// If PERF_FORMAT_GROUP was not specified
struct read_format {
__u64 value; /* The value of the event */
__u64 time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
__u64 time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
__u64 id; /* if PERF_FORMAT_ID */
};
/*
The MMAP events record the PROT_EXEC mappings so that
we can correlate user-space IPs to code. They have
the following structure:
pid is the process ID.
tid is the thread ID.
addr is the address of the allocated memory.
len is the length of the allocated memory.
pgoff is the page offset of the allocated memory.
filename
is a string describing the backing of
the allocated memory.
*/
struct PerfRecordMmapData {
u32 pid, tid;
u64 addr;
u64 len;
u64 pgoff;
char filename[KILO];
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record includes extended information on mmap(2)
calls returning executable mappings. The format is
similar to that of the PERF_RECORD_MMAP record, but
includes extra values that allow uniquely identifying
shared mappings.
pid is the process ID.
tid is the thread ID.
addr is the address of the allocated memory.
len is the length of the allocated memory.
pgoff is the page offset of the allocated memory.
maj is the major ID of the underlying device.
min is the minor ID of the underlying device.
ino is the inode number.
ino_generation
is the inode generation.
prot is the protection information.
flags is the flags information.
filename
is a string describing the backing of the
allocated memory.
*/
struct PerfRecordMmap2Data {
u32 pid;
u32 tid;
u64 addr;
u64 len;
u64 pgoff;
u32 maj;
u32 min;
u64 ino;
u64 ino_generation;
u32 prot;
u32 flags;
char filename[KILO];
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates when events are lost.
id is the unique event ID for the samples that were lost.
lost is the number of events that were lost.
*/
struct PerfRecordLostData {
u64 id;
u64 lost;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates a change in the process name.
pid is the process ID.
tid is the thread ID.
comm is a string containing the new name of the process.
*/
struct PerfRecordCommData {
u32 pid;
u32 tid;
char comm[KILO];
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
// This record indicates a sample.
struct PerfRecordSampleData {
u64 sample_id; /* if PERF_SAMPLE_IDENTIFIER */
u64 ip; /* if PERF_SAMPLE_IP */
u32 pid, tid; /* if PERF_SAMPLE_TID */
u64 time; /* if PERF_SAMPLE_TIME */
u64 addr; /* if PERF_SAMPLE_ADDR */
u64 id; /* if PERF_SAMPLE_ID */
u64 stream_id; /* if PERF_SAMPLE_STREAM_ID */
u32 cpu, res; /* if PERF_SAMPLE_CPU */
u64 period; /* if PERF_SAMPLE_PERIOD */
struct read_format v;
/* if PERF_SAMPLE_READ */
u64 nr; /* if PERF_SAMPLE_CALLCHAIN */
u64 *ips; /* if PERF_SAMPLE_CALLCHAIN */
u32 raw_size; /* if PERF_SAMPLE_RAW */
u8 *raw_data; /* if PERF_SAMPLE_RAW */
u64 bnr; /* if PERF_SAMPLE_BRANCH_STACK */
struct perf_branch_entry *lbr; /* if PERF_SAMPLE_BRANCH_STACK */
u64 user_abi; /* if PERF_SAMPLE_REGS_USER */
u64 reg_mask;
u64 reg_nr;
u64 *user_regs; /* if PERF_SAMPLE_REGS_USER */
u64 stack_size; /* if PERF_SAMPLE_STACK_USER */
u8 *stack_data; /* if PERF_SAMPLE_STACK_USER */
u64 dyn_size; /* if PERF_SAMPLE_STACK_USER && stack_size != 0 */
u64 weight; /* if PERF_SAMPLE_WEIGHT */
u64 data_src; /* if PERF_SAMPLE_DATA_SRC */
u64 transaction; /* if PERF_SAMPLE_TRANSACTION */
u64 intr_abi; /* if PERF_SAMPLE_REGS_INTR */
u64 intr_regs[0]; /* if PERF_SAMPLE_REGS_INTR */
u64 phys_addr; /* if PERF_SAMPLE_PHYS_ADDR */
u64 cgroup; /* if PERF_SAMPLE_CGROUP */
};
/*
This record indicates a process exit event.
*/
struct PerfRecordExitData {
u32 pid, ppid;
u32 tid, ptid;
u64 time;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates a throttle/unthrottle event.
*/
struct PerfRecordThrottleData {
u64 time;
u64 id;
u64 stream_id;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates a fork event.
*/
struct PerfRecordForkData {
u32 pid, ppid;
u32 tid, ptid;
u64 time;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
When using hardware sampling (such as Intel PEBS) this
record indicates some number of samples that may have
been lost.
*/
struct PerfRecordLostSamplesData {
u64 lost;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates which process has initiated an
instruction trace event, allowing tools to properly
correlate the instruction addresses in the AUX buffer
with the proper executable.
pid process ID of the thread starting an
instruction trace.
tid thread ID of the thread starting an instruction
trace.
*/
struct PerfRecordItraceStartData {
u32 pid;
u32 tid;
};
/*
This record reports that new data is available in the
separate AUX buffer region.
aux_offset
offset in the AUX mmap region where the new
data begins.
aux_size
size of the data made available.
flags describes the AUX update.
PERF_AUX_FLAG_TRUNCATED
if set, then the data returned was
truncated to fit the available buffer
size.
PERF_AUX_FLAG_OVERWRITE
if set, then the data returned has
overwritten previous data.
*/
struct PerfRecordAuxData {
u64 aux_offset;
u64 aux_size;
u64 flags;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates a read event.
*/
struct PerfRecordReadData {
u32 pid, tid;
read_format values;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record indicates a context switch has happened.
The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field
indicates whether it was a context switch into or away
from the current process.
*/
struct PerfRecordSwitchData {
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
As with PERF_RECORD_SWITCH this record indicates a
context switch has happened, but it only occurs when
sampling in CPU-wide mode and provides additional
information on the process being switched to/from.
The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field
indicates whether it was a context switch into or away
from the current process.
next_prev_pid
The process ID of the previous (if switching
in) or next (if switching out) process on the
CPU.
next_prev_tid
The thread ID of the previous (if switching in)
or next (if switching out) thread on the CPU.
*/
struct PerfRecordSwitchCpuWideData {
u32 next_prev_pid;
u32 next_prev_tid;
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
/*
This record includes various namespace information of
a process.
pid is the process ID
tid is the thread ID
nr_namespace
is the number of namespaces in this record
Each namespace has dev and inode fields and is
recorded in the fixed position like below:
NET_NS_INDEX=0
Network namespace
UTS_NS_INDEX=1
UTS namespace
IPC_NS_INDEX=2
IPC namespace
PID_NS_INDEX=3
PID namespace
USER_NS_INDEX=4
User namespace
MNT_NS_INDEX=5
Mount namespace
CGROUP_NS_INDEX=6
Cgroup namespace
*/
struct PerfRecordNamespacesData {
u32 pid;
u32 tid;
u64 nr_namespaces;
struct name_space {
u64 dev;
u64 inode;
} namespaces[0];
#if SAMPLE_ID_ALL
struct sample_id sample_id;
#endif
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

193
include/register.h Executable file
View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_REGISTER_H
#define HIPERF_REGISTER_H
#include <linux/perf_event.h>
#include <cinttypes>
#include <string>
#include "utilities.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
// these define copy from kernel uapi
enum perf_event_x86_regs {
PERF_REG_X86_AX,
PERF_REG_X86_BX,
PERF_REG_X86_CX,
PERF_REG_X86_DX,
PERF_REG_X86_SI,
PERF_REG_X86_DI,
PERF_REG_X86_BP,
PERF_REG_X86_SP,
PERF_REG_X86_IP,
PERF_REG_X86_FLAGS,
PERF_REG_X86_CS,
PERF_REG_X86_SS,
PERF_REG_X86_DS,
PERF_REG_X86_ES,
PERF_REG_X86_FS,
PERF_REG_X86_GS,
PERF_REG_X86_R8,
PERF_REG_X86_R9,
PERF_REG_X86_R10,
PERF_REG_X86_R11,
PERF_REG_X86_R12,
PERF_REG_X86_R13,
PERF_REG_X86_R14,
PERF_REG_X86_R15,
PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
};
enum perf_event_arm64_regs {
PERF_REG_ARM64_X0,
PERF_REG_ARM64_X1,
PERF_REG_ARM64_X2,
PERF_REG_ARM64_X3,
PERF_REG_ARM64_X4,
PERF_REG_ARM64_X5,
PERF_REG_ARM64_X6,
PERF_REG_ARM64_X7,
PERF_REG_ARM64_X8,
PERF_REG_ARM64_X9,
PERF_REG_ARM64_X10,
PERF_REG_ARM64_X11,
PERF_REG_ARM64_X12,
PERF_REG_ARM64_X13,
PERF_REG_ARM64_X14,
PERF_REG_ARM64_X15,
PERF_REG_ARM64_X16,
PERF_REG_ARM64_X17,
PERF_REG_ARM64_X18,
PERF_REG_ARM64_X19,
PERF_REG_ARM64_X20,
PERF_REG_ARM64_X21,
PERF_REG_ARM64_X22,
PERF_REG_ARM64_X23,
PERF_REG_ARM64_X24,
PERF_REG_ARM64_X25,
PERF_REG_ARM64_X26,
PERF_REG_ARM64_X27,
PERF_REG_ARM64_X28,
PERF_REG_ARM64_X29,
PERF_REG_ARM64_LR,
PERF_REG_ARM64_SP,
PERF_REG_ARM64_PC,
PERF_REG_ARM64_MAX,
};
enum perf_event_arm_regs {
PERF_REG_ARM_R0,
PERF_REG_ARM_R1,
PERF_REG_ARM_R2,
PERF_REG_ARM_R3,
PERF_REG_ARM_R4,
PERF_REG_ARM_R5,
PERF_REG_ARM_R6,
PERF_REG_ARM_R7,
PERF_REG_ARM_R8,
PERF_REG_ARM_R9,
PERF_REG_ARM_R10,
PERF_REG_ARM_FP = 11,
PERF_REG_ARM_IP = 12,
PERF_REG_ARM_SP = 13,
PERF_REG_ARM_LR = 14,
PERF_REG_ARM_PC = 15,
PERF_REG_ARM_MAX,
};
enum ArchType {
X86_32,
X86_64,
ARM,
ARM64,
UNSUPPORT,
};
// order is IP , SP for ut
static const std::map<size_t, const std::string> PERF_REG_NAME_MAP = {
#if defined(target_cpu_x64)
{PERF_REG_X86_IP, "PERF_REG_X86_IP"},
{PERF_REG_X86_SP, "PERF_REG_X86_SP"},
#elif defined(target_cpu_arm)
{PERF_REG_ARM_PC, "PERF_REG_ARM_PC"},
{PERF_REG_ARM_SP, "PERF_REG_ARM_SP"},
#elif defined(target_cpu_arm64)
{PERF_REG_ARM64_PC, "PERF_REG_ARM64_PC"},
{PERF_REG_ARM64_SP, "PERF_REG_ARM64_SP"},
#endif
};
// context name
static const std::map<uint64_t, const std::string> PERF_CONTEXT_NAME = {
{PERF_CONTEXT_HV, "PERF_CONTEXT_HV"},
{PERF_CONTEXT_KERNEL, "PERF_CONTEXT_KERNEL"},
{PERF_CONTEXT_USER, "PERF_CONTEXT_USER"},
{PERF_CONTEXT_GUEST, "PERF_CONTEXT_GUEST"},
{PERF_CONTEXT_GUEST_KERNEL, "PERF_CONTEXT_GUEST_KERNEL"},
{PERF_CONTEXT_GUEST_USER, "PERF_CONTEXT_GUEST_USER"},
{PERF_CONTEXT_MAX, "PERF_CONTEXT_MAX"},
};
#if defined(target_cpu_x64)
constexpr ArchType buildArchType = ArchType::X86_64;
#elif defined(target_cpu_arm64)
constexpr ArchType buildArchType = ArchType::ARM64;
#elif defined(target_cpu_arm)
constexpr ArchType buildArchType = ArchType::ARM;
#else
#error NOT SUPPORT ARCH
#endif
const std::string UpdatePerfContext(uint64_t addr, perf_callchain_context &perfCallchainContext);
const std::string GetArchName(ArchType arch);
uint64_t GetSupportedRegMask(ArchType arch);
// this is only for debug
const std::string RegisterGetName(size_t registerIndex);
bool RegisterGetValue(uint64_t &value, const u64 registers[], const size_t registerIndex,
const size_t registerNumber);
size_t RegisterGetSP(ArchType arch);
size_t RegisterGetIP(ArchType arch);
inline bool RegisterGetSPValue(uint64_t &value, ArchType arch, const u64 registers[],
const size_t registerNumber)
{
return RegisterGetValue(value, registers, RegisterGetSP(arch), registerNumber);
}
inline bool RegisterGetIPValue(uint64_t &value, ArchType arch, const u64 registers[],
const size_t registerNumber)
{
return RegisterGetValue(value, registers, RegisterGetIP(arch), registerNumber);
}
int LibunwindRegIdToPerfReg(int regnum);
ArchType GetDeviceArch();
ArchType SetDeviceArch(ArchType arch);
ArchType GetArchTypeFromUname(const std::string &machine);
ArchType GetArchTypeFromABI(bool abi32);
void UpdateRegForABI(ArchType arch, u64 registers[]);
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

573
include/report.h Executable file
View File

@ -0,0 +1,573 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef REPORT_H
#define REPORT_H
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <linux/perf_event.h>
#include <map>
#include <optional>
#include <set>
#include <stdio.h>
#include "debug_logger.h"
#include "utilities.h"
#include "virtual_runtime.h"
// remove me latter
#include "report_json_file.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class ReportItemCallFrame {
public:
std::string_view func_;
uint64_t vaddr_;
std::string_view dso_;
uint64_t eventCount_ = 0; // call chain event
uint64_t selfEventCount_ = 0; // call chain event end in this function
std::vector<ReportItemCallFrame> childs;
ReportItemCallFrame(std::string_view func, uint64_t vaddr, std::string_view dso,
uint64_t eventCount, uint64_t selfEventCount)
: func_(func),
vaddr_(vaddr),
dso_(dso),
eventCount_(eventCount),
selfEventCount_(selfEventCount)
{
}
bool operator==(const ReportItemCallFrame &b) const
{
return Same(b);
}
bool operator!=(const ReportItemCallFrame &b) const
{
return !Same(b);
}
static int CompareSortingEventCount(const ReportItemCallFrame &a, const ReportItemCallFrame &b)
{
return a.eventCount_ > b.eventCount_;
}
static void OrderCallFrames(std::vector<ReportItemCallFrame> &callframes, int indent = 2)
{
int i = 2;
if (callframes.size() > 0) {
std::sort(callframes.begin(), callframes.end(),
&ReportItemCallFrame::CompareSortingEventCount);
for (auto &callframe : callframes) {
HLOGDUMMY("%*s%s", indent, "", callframe.ToDebugString().c_str());
if (callframe.childs.size() > 0) {
OrderCallFrames(callframe.childs, indent + i);
}
}
}
}
// just a log
static void DumpCallFrames(std::vector<ReportItemCallFrame> &callframes, int indent = 2)
{
int y = 2;
if (callframes.size() > 0) {
for (auto &callframe : callframes) {
HLOGV("%*s%s", indent, "", callframe.ToDebugString().c_str());
if (callframe.childs.size() > 0) {
DumpCallFrames(callframe.childs, indent + y);
}
}
}
}
const std::string ToDebugString() const
{
return StringPrintf("%" PRIu64 "(%" PRIu64 ")%s(%s+0x%" PRIx64 ") child %zu", eventCount_,
selfEventCount_, func_.data(), dso_.data(), vaddr_, childs.size());
}
private:
bool Same(const ReportItemCallFrame &b) const
{
return (func_ == b.func_) and (vaddr_ == b.vaddr_) and (dso_ == b.dso_);
}
};
// one item or one line in report
class ReportItem {
public:
pid_t pid_ = 0;
pid_t tid_ = 0;
std::string_view comm_ = "";
std::string_view dso_ = "";
std::string_view fromDso_ = "";
std::string_view fromFunc_ = "";
std::string_view func_ = "";
uint64_t vaddr_ = 0;
uint64_t eventCount_ = 0; // event count
std::vector<ReportItemCallFrame> callStacks_;
float heat = 0.0f;
static unsigned long long allIndex_; // debug only
unsigned long long index_;
// only for ut test
ReportItem(pid_t pid, pid_t tid, const char *comm, const char *dso, const char *func,
uint64_t vaddr, uint64_t eventCount)
: pid_(pid),
tid_(tid),
comm_(comm),
dso_(dso),
func_(func),
vaddr_(vaddr),
eventCount_(eventCount)
{
HLOG_ASSERT(comm != nullptr);
index_ = allIndex_++;
}
ReportItem(pid_t pid, pid_t tid, std::string &comm, const std::string_view &dso,
const std::string_view &func, uint64_t vaddr, uint64_t eventCount)
: pid_(pid),
tid_(tid),
comm_(comm),
dso_(dso),
func_(func),
vaddr_(vaddr),
eventCount_(eventCount)
{
HLOG_ASSERT(!comm.empty());
index_ = allIndex_++;
}
bool operator==(const ReportItem &b) const
{
return Same(b);
}
bool operator!=(const ReportItem &b) const
{
return !Same(b);
}
// debug only
const std::string ToDebugString() const
{
return StringPrintf("%d:%d:%s-%s(%s):%zu i:%llu", pid_, tid_, comm_.data(), func_.data(),
dso_.data(), eventCount_, index_);
}
// Count
static int CompareEventCount(const ReportItem &a, const ReportItem &b)
{
if (a.eventCount_ != b.eventCount_) {
return (a.eventCount_ > b.eventCount_) ? 1 : -1;
} else {
return 0;
}
}
static int CompareSortingEventCount(const ReportItem &a, const ReportItem &b)
{
return a.eventCount_ > b.eventCount_;
}
static const std::string GetEventCount(const ReportItem &a, size_t len,
const std::string &format)
{
return StringPrintf(format.c_str(), len, a.eventCount_);
}
// Pid
static int ComparePid(const ReportItem &a, const ReportItem &b)
{
if (a.pid_ != b.pid_) {
return (a.pid_ > b.pid_) ? 1 : -1;
} else {
return 0;
}
}
static const std::string GetPid(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.pid_);
}
// Tid
static int CompareTid(const ReportItem &a, const ReportItem &b)
{
if (a.tid_ != b.tid_) {
return (a.tid_ > b.tid_) ? 1 : -1;
} else {
return 0;
}
}
static const std::string GetTid(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.tid_);
}
// Comm
static int CompareComm(const ReportItem &a, const ReportItem &b)
{
int result = a.comm_.compare(b.comm_);
return result;
}
static const std::string GetComm(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.comm_.data());
}
// Func
static int CompareFunc(const ReportItem &a, const ReportItem &b)
{
return a.func_.compare(b.func_);
}
static const std::string GetFunc(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.func_.data());
}
// Dso
static int CompareDso(const ReportItem &a, const ReportItem &b)
{
return a.dso_.compare(b.dso_);
}
static const std::string GetDso(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.dso_.data());
}
// fromDso
static int CompareFromDso(const ReportItem &a, const ReportItem &b)
{
return a.fromDso_.compare(b.fromDso_);
}
static const std::string GetFromDso(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.fromDso_.data());
}
// fromFunc
static int CompareFromFunc(const ReportItem &a, const ReportItem &b)
{
return a.fromFunc_.compare(b.fromFunc_);
}
static const std::string GetFromFunc(const ReportItem &a, size_t len, const std::string &format)
{
return StringPrintf(format.c_str(), len, a.fromFunc_.data());
}
private:
bool Same(const ReportItem &b) const
{
return (comm_ == b.comm_) && (pid_ == b.pid_) && (tid_ == b.tid_) && (func_ == b.func_) &&
(dso_ == b.dso_) && (vaddr_ == b.vaddr_);
}
};
using ReportKeyCompareFunction = int(const ReportItem &, const ReportItem &);
using ReportKeyGetFunction = const std::string(const ReportItem &, size_t, const std::string &);
constexpr const int MAX_FILED_LEN = 20;
constexpr const int CALLSTACK_INDENT = 4;
struct ReportKey {
const std::string keyName_;
const std::string valueFormat_;
size_t maxLen_ = 0u;
std::string maxValue_;
ReportKeyCompareFunction &compareFunction_;
ReportKeyGetFunction &GetFunction_;
const std::vector<std::string> &displayFilter_;
ReportKey(const std::string keyName, ReportKeyCompareFunction &compareFunction,
ReportKeyGetFunction &GetFunction, const std::string valueFormat,
const std::vector<std::string> &displayFilter)
: keyName_(keyName),
valueFormat_(valueFormat),
compareFunction_(compareFunction),
GetFunction_(GetFunction),
displayFilter_(displayFilter)
{
maxLen_ = keyName.size();
}
void UpdateValueMaxLen(const std::string &value)
{
size_t newMaxLen = std::max(maxLen_, value.size());
if (maxLen_ < newMaxLen) {
maxValue_ = value;
maxLen_ = newMaxLen;
}
}
void UpdateValueMaxLen(size_t value)
{
size_t newMaxLen = std::max(maxLen_, std::to_string(value).size());
if (maxLen_ < newMaxLen) {
maxValue_ = std::to_string(value);
maxLen_ = newMaxLen;
}
}
std::string GetValue(const ReportItem &i)
{
return GetFunction_(i, maxLen_, valueFormat_);
}
bool ShouldDisplay(const ReportItem &i)
{
if (displayFilter_.size() == 0) {
return true;
} else {
std::string value = GetFunction_(i, 0, valueFormat_);
auto it = find(displayFilter_.begin(), displayFilter_.end(), value);
if (it == displayFilter_.end()) {
HLOGV(" not found '%s' in %s", value.c_str(),
VectorToString(displayFilter_).c_str());
}
return (it != displayFilter_.end());
}
}
};
using ReportItems = std::vector<ReportItem>;
using ReportItemsIt = ReportItems::iterator;
using ReportItemsConstIt = ReportItems::const_iterator;
struct ReportOption {
float heatLimit_ = 0.0f;
float callStackHeatLimit_ = 0.0f;
// display filter
std::vector<std::string> displayComms_ {};
std::vector<std::string> displayPids_ {};
std::vector<std::string> displayTids_ {};
std::vector<std::string> displayDsos_ {};
std::vector<std::string> displayFromDsos_ {};
std::vector<std::string> displayFuncs_ {};
std::vector<std::string> displayFromFuncs_ {};
std::vector<std::string> displayDummy_ {};
std::vector<std::string> sortKeys_ = {"comm", "pid", "tid", "dso", "func"};
bool debug_ = false;
bool hideCount_ = false;
};
class Report {
public:
Report() : option_(defaultOption_), virtualRuntime_(false)
{
// works for ut test
}
Report(ReportOption &option) : option_(option), virtualRuntime_(false) {}
bool MultiLevelSame(const ReportItem &a, const ReportItem &b);
void AdjustReportItems();
void AddReportItem(const PerfRecordSample &sample, bool includeCallStack);
void AddReportItemBranch(const PerfRecordSample &sample);
void OutputStd(FILE *output);
void OutputStdDiff(FILE *output, Report &other);
ReportOption &option_;
VirtualRuntime virtualRuntime_;
std::map<std::string, ReportKey> reportKeyMap_ = {
{
"count",
{
"count",
ReportItem::CompareEventCount,
ReportItem::GetEventCount,
"%*" PRIu64 "",
option_.displayDummy_,
},
},
{
"comm",
{
"comm",
ReportItem::CompareComm,
ReportItem::GetComm,
"%-*s",
option_.displayComms_,
},
},
{
"pid",
{
"pid",
ReportItem::ComparePid,
ReportItem::GetPid,
"%*d",
option_.displayPids_,
},
},
{
"tid",
{
"tid",
ReportItem::CompareTid,
ReportItem::GetTid,
"%*d",
option_.displayTids_,
},
},
{
"dso",
{
"dso",
ReportItem::CompareDso,
ReportItem::GetDso,
"%-*s",
option_.displayDsos_,
},
},
{
"from_dso",
{
"from_dso",
ReportItem::CompareFromDso,
ReportItem::GetFromDso,
"%-*s",
option_.displayFromDsos_,
},
},
{
"func",
{
"func",
ReportItem::CompareFunc,
ReportItem::GetFunc,
"%-*s",
option_.displayFuncs_,
},
},
{
"from_func",
{
"from_func",
ReportItem::CompareFromFunc,
ReportItem::GetFromFunc,
"%-*s",
option_.displayFromFuncs_,
},
},
};
struct ReportEventConfigItem {
ReportEventConfigItem(const ReportEventConfigItem &) = delete;
ReportEventConfigItem &operator=(const ReportEventConfigItem &) = delete;
ReportEventConfigItem(ReportEventConfigItem &&) = default;
std::string eventName_;
uint64_t sampleCount_ = 0;
uint64_t eventCount_ = 0;
std::vector<ReportItem> reportItems_;
uint32_t type_;
uint64_t config_;
std::vector<uint64_t> ids_;
bool coutMode_ = true; // use cout or time ?
bool operator==(const ReportEventConfigItem &o) const
{
return (type_ == o.type_) && (config_ == o.config_);
}
bool operator!=(const ReportEventConfigItem &o) const
{
return !(operator==(o));
}
std::string toDebugString()
{
return StringPrintf("%s(%" PRIu32 "-%" PRIu64 "):PRIu64", eventName_.c_str(), type_,
config_, sampleCount_);
}
ReportEventConfigItem(std::string eventName, uint32_t type, uint64_t config,
bool coutMode = true)
: eventName_(eventName), type_(type), config_(config), coutMode_(coutMode)
{
}
};
std::vector<ReportEventConfigItem> configs_;
virtual ~Report() {};
std::map<uint64_t, size_t> configIdIndexMaps_; // index of configNames_
std::string GetConfigName(uint64_t id)
{
return configs_[GetConfigIndex(id)].eventName_;
}
size_t GetConfigIndex(uint64_t id)
{
HLOG_ASSERT_MESSAGE(configIdIndexMaps_.find(id) != configIdIndexMaps_.end(),
"unable found id %" PRIx64 "", id);
return configIdIndexMaps_.at(id);
}
private:
FILE *output_ = nullptr;
const std::string TEXT_RED = "\x1b[31m";
const std::string TEXT_GREEN = "\x1b[32m";
const std::string TEXT_RESET = "\033[0m";
const int ConsoleDefaultWidth = 80;
// sometime caller dont give the option
ReportOption defaultOption_;
std::vector<std::string> displayKeyNames_;
// use virtual only for gmock test
bool MultiLevelSorting(const ReportItem &a, const ReportItem &b);
bool MultiLevelSameAndUpdateCount(ReportItem &l, ReportItem &r);
void MergeCallFrameCount(ReportItem &l, ReportItem &r);
virtual int MultiLevelCompare(const ReportItem &a, const ReportItem &b);
void StatisticsRecords();
void FilterDisplayRecords();
void UpdateReportItemsAfterAdjust();
// std out
int consoleWidth_ = 0;
void PrepareConsole();
void OutputStdStatistics(ReportEventConfigItem &);
bool OutputStdStatistics(ReportEventConfigItem &config, ReportEventConfigItem &otherConfig);
void OutputStdHead(ReportEventConfigItem &, bool diffMode = false);
void OutputStdContent(ReportEventConfigItem &);
void OutputStdContentDiff(ReportEventConfigItem &, ReportEventConfigItem &);
void OutputStdContentItem(const ReportItem &reportItem);
void OutputStdContentDiffOneSide(bool leftOnly, ReportItem &reportItem);
void OutputStdCallFrames(int indent, const ReportItemCallFrame &callFrames,
uint64_t totalEventCount);
bool OutputStdCallFrame(int indent, const std::string_view &funcName, uint64_t eventCount,
uint64_t totalEventCount);
void OutputStdItemHeating(float heat, float heat2);
FRIEND_TEST(ReportTest, MultiLevelSorting);
FRIEND_TEST(ReportTest, MultiLevelSameAndUpdateCount);
FRIEND_TEST(ReportTest, MergeCallFrameCount);
FRIEND_TEST(ReportTest, MultiLevelCompare);
FRIEND_TEST(ReportTest, PrepareConsole);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

379
include/report_json_file.h Executable file
View File

@ -0,0 +1,379 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef REPORT_JSON_FILE_H
#define REPORT_JSON_FILE_H
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <linux/perf_event.h>
#include <map>
#include <optional>
#include <set>
#include <stdio.h>
#include "debug_logger.h"
#include "perf_file_reader.h"
#include "utilities.h"
#include "virtual_runtime.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
using jsonStringMap = std::map<std::string, std::string>;
using jsonStringVector = std::vector<std::string>;
using jsonIntVector = std::vector<int>;
template<class T>
void OutputJsonKey(FILE *output, const T &value)
{
if constexpr (std::is_same<T, std::string>::value) {
if (value.empty()) {
// for key vector [] mode, not key is needed
return;
}
fprintf(output, "\"%s\":", value.c_str());
} else if constexpr (std::is_same<T, std::string_view>::value) {
if (value.empty()) {
// for key vector [] mode, not key is needed
return;
}
fprintf(output, "\"%s\":", value.data());
} else if constexpr (std::is_same<typename std::decay<T>::type, char *>::value) {
if (value[0] == '\0') {
// same as value.empty()
return;
}
fprintf(output, "\"%s\":", value);
} else {
fprintf(output, "\"%s\":", std::to_string(value).c_str());
}
}
template<class T>
void OutputJsonValue(FILE *output, const T &value, bool first = true)
{
if (!first) {
fprintf(output, ",");
}
if constexpr (std::is_same<T, std::string>::value) {
fprintf(output, "\"%s\"", value.c_str());
} else if constexpr (std::is_same<T, std::string_view>::value) {
fprintf(output, "\"%s\"", value.data());
} else if constexpr (std::is_same<T, int>::value) {
fprintf(output, "%s", std::to_string(value).c_str());
} else if constexpr (std::is_same<T, uint64_t>::value) {
fprintf(output, "%s", std::to_string(value).c_str());
} else if constexpr (std::is_same<T, bool>::value) {
fprintf(output, "%s", std::to_string(value).c_str());
} else if constexpr (std::is_same<T, size_t>::value) {
fprintf(output, "%s", std::to_string(value).c_str());
} else if constexpr (std::is_same<typename std::decay<T>::type, char *>::value) {
fprintf(output, "\"%s\"", value);
} else {
value.OutputJson(output);
}
}
/*
k:"v"
k:1
*/
template<class K, class T>
void OutputJsonPair(FILE *output, const K &key, const T &value, bool first = false)
{
if (!first) {
fprintf(output, ",");
}
OutputJsonKey(output, key);
OutputJsonValue(output, value);
}
/*
k:[v1,v2,v3]
*/
template<class T>
void OutputJsonVectorList(FILE *output, const std::string &key, const std::vector<T> &value,
bool first = false)
{
if (!first) {
fprintf(output, ",");
}
fprintf(output, "\"%s\":", key.c_str());
fprintf(output, "[");
auto it = value.begin();
while (it != value.end()) {
OutputJsonValue(output, *it, it == value.begin());
it++;
}
fprintf(output, "]");
}
/*
k:[v1,v2,v3]
*/
template<class K, class V>
void OutputJsonMapList(FILE *output, const std::string &key, const std::map<K, V> &value,
bool first = false)
{
if (!first) {
fprintf(output, ",");
}
fprintf(output, "\"%s\":", key.c_str());
fprintf(output, "[");
auto it = value.begin();
while (it != value.end()) {
OutputJsonValue(output, it->second, it == value.begin());
it++;
}
fprintf(output, "]");
}
/*
k:{k1:v1,k2:v2,k3:v3}
*/
template<class K, class V>
void OutputJsonMap(FILE *output, const std::string &key, const std::map<K, V> &value,
bool first = false)
{
if (!first) {
fprintf(output, ",");
}
fprintf(output, "\"%s\":", key.c_str());
fprintf(output, "{");
auto it = value.begin();
while (it != value.end()) {
OutputJsonPair(output, it->first, it->second, it == value.begin());
it++;
}
fprintf(output, "}");
}
template<class K, class V>
V &GetOrCreateMapItem(std::map<K, V> &map, const K &key)
{
if (map.count(key) == 0) {
map.emplace(key, (key));
return map.at(key);
} else {
return map.at(key);
}
}
struct ReportFuncMapItem {
int libId_ = -1;
std::string_view funcName_;
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "file", libId_, true);
OutputJsonPair(output, "symbol", funcName_);
fprintf(output, "}");
}
ReportFuncMapItem(int libId, std::string_view funcName) : libId_(libId), funcName_(funcName) {}
};
struct ReportFuncItem {
int functionId_ = -1;
int functionInLibId_ = -1;
uint64_t sampleCount_ = 0;
uint64_t eventCount_ = 0;
uint64_t subTreeEventCount_ = 0;
ReportFuncItem(int functionId) : functionId_(functionId) {}
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "symbol", functionId_, true);
OutputJsonVectorList(output, "counts",
std::vector<uint64_t> {sampleCount_, eventCount_, subTreeEventCount_});
fprintf(output, "}");
}
};
struct ReportCallNodeItem {
uint64_t selfEventCount_ = 0;
uint64_t subTreeEventCount_ = 0;
int functionId_ = -1;
int nodeIndex_ = -1;
bool reverseCaller_ = false;
std::string_view funcName_ = "";
std::string debug_ = "";
std::map<int, ReportCallNodeItem> childrenMap;
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "selfEvents", selfEventCount_, true);
OutputJsonPair(output, "subEvents", subTreeEventCount_);
OutputJsonPair(output, "symbol", functionId_);
if (!funcName_.empty()) { // for debug
OutputJsonPair(output, "funcName", funcName_);
OutputJsonPair(output, "nodeIndex", nodeIndex_);
OutputJsonPair(output, "reversed", reverseCaller_);
}
OutputJsonMapList(output, "callStack", childrenMap);
fprintf(output, "}");
}
uint64_t UpdateChildrenEventCount()
{
subTreeEventCount_ = selfEventCount_;
for (auto &pair : childrenMap) {
subTreeEventCount_ += pair.second.UpdateChildrenEventCount();
if (!funcName_.empty()) {
}
}
return subTreeEventCount_;
}
static bool FindByFunctionId(ReportCallNodeItem &a, int functionId)
{
return (a.functionId_ == functionId);
}
ReportCallNodeItem(int functionId) : functionId_(functionId) {}
};
struct ReportLibItem {
int libId_ = 0;
uint64_t eventCount_ = 0;
std::map<int, ReportFuncItem> funcs_;
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "fileId", libId_, true);
OutputJsonPair(output, "eventCount", eventCount_);
OutputJsonMapList(output, "functions", funcs_);
fprintf(output, "}");
}
};
struct ReportThreadItem {
pid_t tid_ = 0;
uint64_t eventCount_ = 0;
uint64_t sampleCount_ = 0;
std::map<int, ReportLibItem> libs_;
ReportCallNodeItem callNode;
ReportCallNodeItem callNodeReverse;
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "tid", tid_, true);
OutputJsonPair(output, "eventCount", eventCount_);
OutputJsonPair(output, "sampleCount", sampleCount_);
OutputJsonMapList(output, "libs", libs_);
OutputJsonPair(output, "CallOrder", callNode);
OutputJsonPair(output, "CalledOrder", callNodeReverse);
fprintf(output, "}");
}
ReportThreadItem(pid_t id) : tid_(id), callNode(-1), callNodeReverse(-1) {}
};
struct ReportProcessItem {
pid_t pid_ = 0;
uint64_t eventCount_ = 0;
std::map<pid_t, ReportThreadItem> threads_;
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "pid", pid_, true);
OutputJsonPair(output, "eventCount", eventCount_);
OutputJsonMapList(output, "threads", threads_);
fprintf(output, "}");
}
ReportProcessItem(pid_t pid) : pid_(pid) {}
};
struct ReportConfigItem {
int index_;
std::string eventName_;
uint64_t eventCount_ = 0;
std::map<pid_t, ReportProcessItem> processes_;
void OutputJson(FILE *output) const
{
fprintf(output, "{");
OutputJsonPair(output, "eventConfigName", eventName_, true);
OutputJsonPair(output, "eventCount", eventCount_);
OutputJsonMapList(output, "processes", processes_);
fprintf(output, "}");
}
ReportConfigItem(int index, std::string eventName) : index_(index), eventName_(eventName) {}
};
using functionKey = std::tuple<int, std::string>;
static constexpr const int keyLibId = 0;
static constexpr const int keyfuncName = 1;
class ReportJsonFile {
public:
int nodeIndex_ = 0; // debug only
static bool debug_;
FILE *output_ = nullptr;
ReportJsonFile(const std::unique_ptr<PerfFileReader> &recordFileReader,
const VirtualRuntime &virtualRuntime)
: recordFileReader_(recordFileReader), virtualRuntime_(virtualRuntime)
{
}
void UpdateReportSample(uint64_t configid, pid_t pid, pid_t tid, uint64_t eventCount);
void UpdateReportCallStack(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount,
std::vector<CallFrame> &frames);
void UpdateCallNodeEventCount();
void ProcessSymbolsFiles(const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles);
// json
bool OutputJson(FILE *output = nullptr);
std::map<std::vector<uint64_t>, ReportConfigItem> reportConfigItems_;
private:
const std::unique_ptr<PerfFileReader> &recordFileReader_;
const VirtualRuntime &virtualRuntime_;
std::vector<std::string_view> libList_;
std::vector<functionKey> functionList_;
std::map<int, ReportFuncMapItem> functionMap_;
void addNewFunction(int libId, std::string_view name);
ReportConfigItem &GetConfig(uint64_t id);
std::string GetConfigName(uint64_t id);
uint32_t GetConfigIndex(uint64_t id);
int GetFuncionID(int libId, std::string_view function);
int GetLibID(std::string_view filepath);
void OutputJsonFeatureString();
void OutputJsonRuntimeInfo();
void AddReportCallStack(uint64_t eventCount, ReportCallNodeItem &callNode,
const std::vector<CallFrame> &frames);
void AddReportCallStackReverse(uint64_t eventCount, ReportCallNodeItem &callNode,
const std::vector<CallFrame> &frames);
uint64_t sampleCount_ = 0;
FRIEND_TEST(ReportJsonFileTest, UpdateReportSample);
FRIEND_TEST(ReportJsonFileTest, UpdateReportCallStack);
FRIEND_TEST(ReportJsonFileTest, UpdateCallNodeEventCount);
FRIEND_TEST(ReportJsonFileTest, ProcessSymbolsFiles);
FRIEND_TEST(ReportJsonFileTest, GetFuncionID);
FRIEND_TEST(ReportJsonFileTest, GetLibID);
FRIEND_TEST(ReportJsonFileTest, GetConfigIndex);
FRIEND_TEST(ReportJsonFileTest, GetConfigName);
FRIEND_TEST(ReportJsonFileTest, GetConfig);
friend class ReportJsonFileTest;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

100
include/report_protobuf_file.h Executable file
View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_REPORT_PROTOBUF_FILE
#define HIPERF_REPORT_PROTOBUF_FILE
#include <fstream>
#include <linux/perf_event.h>
#include <stdint.h>
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "debug_logger.h"
#include "perf_event_record.h"
#include "report_sample.pb.h"
#include "symbols_file.h"
#include "utilities.h"
namespace Proto = OHOS::Developtools::Hiperf::Proto;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
static const char FILE_MAGIC[] = "HIPERF_PB_";
static const uint16_t FILE_VERSION = 1u;
using ProtobufReadBack = std::function<void(Proto::HiperfRecord &record)>;
class ReportProtobufFileWriter : public google::protobuf::io::CopyingOutputStream {
public:
bool Create(std::string fileName);
bool ProcessRecord(const PerfEventRecord &record);
bool ProcessSampleRecord(const PerfRecordSample &recordSample, uint32_t configIndex,
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles);
bool ProcessSymbolsFiles(const std::vector<std::unique_ptr<SymbolsFile>> &);
bool ProcessReportInfo(const std::vector<std::string> &configNames,
const std::string &workloadCmd);
~ReportProtobufFileWriter();
void Close();
private:
std::unique_ptr<google::protobuf::io::CopyingOutputStreamAdaptor> protpbufOutputStream_;
std::unique_ptr<google::protobuf::io::CodedOutputStream> protpbufCodedOutputStream_;
std::string fileName_;
std::unique_ptr<std::ofstream> protobufFileStream_ = std::make_unique<std::ofstream>();
uint64_t recordCount_ = 0;
uint64_t recordLost_ = 0;
bool isOpen();
bool Write(const void *buffer, int size) override;
virtual bool ProcessRecord(const PerfRecordComm &);
virtual bool ProcessRecord(const PerfRecordLost &);
void BeforeClose();
FRIEND_TEST(ReportProtobufFileTest, Close);
FRIEND_TEST(ReportProtobufFileTest, ProcessRecord);
FRIEND_TEST(ReportProtobufFileTest, ProcessRecordPerfRecordLost);
FRIEND_TEST(ReportProtobufFileTest, ProcessRecordPerfRecordComm);
FRIEND_TEST(ReportProtobufFileTest, BeforeClose);
FRIEND_TEST(ReportProtobufFileTest, ProcessSymbolsFiles);
};
class ReportProtobufFileReader : public google::protobuf::io::CopyingInputStream {
public:
bool Dump(std::string fileName, ProtobufReadBack readBack = nullptr);
private:
std::unique_ptr<google::protobuf::io::CopyingInputStreamAdaptor> protpbufInputStream_;
std::unique_ptr<google::protobuf::io::CodedInputStream> protpbufCodedInputStream_;
std::string fileName_;
std::unique_ptr<std::ifstream> protobufFileStream_ = std::make_unique<std::ifstream>();
bool isOpen();
bool CheckFileMagic();
int Read(void *buffer, int size) override;
bool Dump(const Proto::HiperfRecord &record, int indent = 0);
bool Dump(const Proto::CallStackSample &message, int indent = 0);
bool Dump(const Proto::SampleStatistic &message, int indent = 0);
bool Dump(const Proto::SymbolTableFile &message, int indent = 0);
bool Dump(const Proto::VirtualThreadInfo &message, int indent = 0);
bool Dump(const Proto::ReportInfo &message, int indent = 0);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

54
include/ring_buffer.h Executable file
View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_RING_BUFFER_H
#define HIPERF_RING_BUFFER_H
#include <atomic>
#include <memory>
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class RingBuffer {
public:
// little endian, perf_event_header.type is less than 0xff, so set it
static constexpr uint8_t MARGIN_BYTE = 0xFF;
RingBuffer(size_t size);
~RingBuffer();
// get size of the writable space
size_t GetFreeSize() const;
// before writing data to rbuff, alloc space first
uint8_t *AllocForWrite(size_t writeSize);
// after writing data, move head pointer
void EndWrite();
// get data from buff, return nullptr if no readable data
uint8_t *GetReadData();
// after reading, move tail pointer
void EndRead();
private:
std::unique_ptr<uint8_t[]> buf_ = nullptr;
const size_t size_;
std::atomic_size_t head_ = 0; // write after this, always increase
std::atomic_size_t tail_ = 0; // read from this, always increase
size_t writeSize_ = 0;
size_t readSize_ = 0;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

102
include/subcommand.h Executable file
View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_SUBCOMMAND_H_
#define HIPERF_SUBCOMMAND_H_
#include "utilities.h"
#include <cstdlib>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class SubCommand {
public:
SubCommand(const std::string &name, const std::string &brief, const std::string &help)
: name_(name), brief_(brief), help_(help)
{
}
virtual ~SubCommand() {}
const std::string &Name() const
{
return name_;
}
const std::string &Brief() const
{
return brief_;
}
const std::string &Help() const
{
return help_;
}
// parse option first
bool OnSubCommandOptions(std::vector<std::string> args);
// some help cmd
bool OnPreSubCommand()
{
if (showHelp_) {
printf("%s\n", Help().c_str());
return true;
}
return false;
};
virtual void DumpOptions() const {};
// args should be empty after all the args processed
virtual bool ParseOption(std::vector<std::string> &args)
{
args.clear(); // all the args is processed
return true;
}
// return false means cmd failed
virtual bool OnSubCommand(std::vector<std::string> &args) = 0;
// some test code will use this for simple
bool OnSubCommand(std::string stringArgs)
{
auto args = StringSplit(stringArgs, " ");
return OnSubCommand(args);
};
// called from main
static bool RegisterSubCommand(std::string, std::unique_ptr<SubCommand>);
// get some cmd
static const std::map<std::string, std::unique_ptr<SubCommand>> &GetSubCommands();
static SubCommand *FindSubCommand(std::string);
// for test code
static void ClearSubCommands();
protected:
const std::string name_;
const std::string brief_;
std::string help_;
bool dumpOptions_ = false;
bool showHelp_ = false;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

118
include/subcommand_dump.h Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef SUBCOMMAND_DUMP_H
#define SUBCOMMAND_DUMP_H
#include "perf_file_reader.h"
#include <memory>
#if HAVE_PROTOBUF
#include "report_protobuf_file.h"
#endif
#include "subcommand.h"
#include "symbols_file.h"
#include "virtual_runtime.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
static const std::string DEFAULT_DUMP_FILENAME = "perf.data";
class SubCommandDump : public SubCommand {
public:
SubCommandDump()
// clang-format off
: SubCommand("dump", "Dump content of a perf data file, like perf.data",
"Usage: hiperf dump [option] <filename>\n"
" Dump specific parts of specified file .\n"
" --head\n"
" Dump header and attrs only.\n"
" -d\n"
" Dump data section only.\n"
" -f\n"
" Dump addtional features only.\n"
" --sympath <symbols path>\n"
" use symbols path to find symbols.\n"
" --elf <elf file name>\n"
" dump elf not perf data.\n"
#if HAVE_PROTOBUF
" --proto <protobuf file name>\n"
" dump perf data from protobuf file.\n"
#endif
" --export <sample index>\n"
" also export the user stack data to some split file,\n"
" use this command to produce ut data."
" named with sample index(0 base):\n"
" hiperf_<pid>_<tid>_user_regs_<index>.dump\n"
" hiperf_<pid>_<tid>_user_data_<index>.dump\n"
" <file name>\n"
" perf data file to dump, default is perf.data\n\n"
)
// clang-format on
{
}
bool OnSubCommand(std::vector<std::string> &args) override;
bool ParseOption(std::vector<std::string> &args) override;
static bool RegisterSubCommandDump(void);
static void DumpPrintEventAttr(const perf_event_attr &attr, int indent = 0);
std::unique_ptr<PerfFileReader> reader_;
private:
static void DumpSampleType(uint64_t sampleType, int indent);
int exportSampleIndex_ = -1;
int currectSampleIndex_ = 0;
std::string dumpFileName_;
std::string elfFileName_;
std::string protobufDumpFileName_;
int indent_ = 0;
#if HAVE_PROTOBUF
std::unique_ptr<ReportProtobufFileReader> protobufInputFileReader_ = nullptr;
#endif
std::vector<AttrWithId> attrIds_;
bool dumpHeader_ = false;
bool dumpFeatures_ = false;
bool dumpData_ = false;
bool dumpAll_ = true;
std::vector<std::string> dumpSymbolsPaths_;
bool CheckInputFile();
bool DumpElfFile();
#if HAVE_PROTOBUF
bool DumpProtoFile();
#endif
void DumpPrintFileHeader(int indent = 0);
void DumpAttrPortion(int indent = 0);
void DumpDataPortion(int indent = 0);
void DumpCallChain(int indent, std::unique_ptr<PerfRecordSample> &sample);
void DumpFeaturePortion(int indent = 0);
void ExprotUserData(std::unique_ptr<PerfEventRecord> &record);
void ExprotUserStack(const PerfRecordSample &recordSample);
void PrintHeaderInfo(const int &indent);
void PrintSymbolFile(const int &indent, const SymbolFileStruct &symbolFileStruct);
void PrintFeatureEventdesc(int indent, const PerfFileSectionEventDesc &sectionEventdesc);
VirtualRuntime vr_;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

45
include/subcommand_help.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_SUBCOMMAND_HELP_H_
#define HIPERF_SUBCOMMAND_HELP_H_
#include "option.h"
#include "subcommand.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class SubCommandHelp : public SubCommand {
public:
SubCommandHelp()
// clang-format off
: SubCommand("help", "Show more help information for hiperf",
"Usage: hiperf help [subcommand]\n"
" By default, all options help information and subcommand brief information are output.\n"
" If you provide a subcommand, only the help information for this command will be output.\n\n"
)
// clang-format on
{
Option::RegisterMainOption("--help", "show help", OnHelp);
Option::RegisterMainOption("-h", "show help", OnHelp);
}
bool OnSubCommand(std::vector<std::string> &args) override;
static void RegisterSubCommandHelp(void);
static bool OnHelp(std::vector<std::string> &args);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

69
include/subcommand_list.h Normal file
View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef SUBCOMMAND_LIST_H_
#define SUBCOMMAND_LIST_H_
#include "subcommand.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "debug_logger.h"
#include "option.h"
#include "perf_events.h"
#include "subcommand_list.h"
#include "utilities.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class SubCommandList : public SubCommand {
public:
SubCommandList()
// clang-format off
: SubCommand("list", "List the supported event types.",
"Usage: hiperf list [event type name]\n"
" List all supported event types on this devices.\n"
" To list the events of a specific type, specify the type name\n"
" hw hardware events\n"
" sw software events\n"
" tp tracepoint events\n"
" cache hardware cache events\n"
" raw raw pmu events\n\n"
)
// clang-format on
{
}
bool OnSubCommand(std::vector<std::string> &args) override;
static void RegisterSubCommandList(void);
private:
PerfEvents perfEvents_;
const std::map<std::string, perf_type_id> SUPPORT_NAME_OPTIONS = {
{"hw", PERF_TYPE_HARDWARE}, {"sw", PERF_TYPE_SOFTWARE}, {"tp", PERF_TYPE_TRACEPOINT},
{"cache", PERF_TYPE_HW_CACHE}, {"raw", PERF_TYPE_RAW},
};
bool ShowSupportEventsTypes(std::vector<perf_type_id> requestEventTypes);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

306
include/subcommand_record.h Normal file
View File

@ -0,0 +1,306 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef SUBCOMMAND_RECORD_H
#define SUBCOMMAND_RECORD_H
// some debug feaure
#define HIDEBUG_RECORD_NOT_PROCESS 0
#define HIDEBUG_RECORD_NOT_PROCESS_VM 0
#define HIDEBUG_RECORD_NOT_SAVE 0
#define HIDEBUG_SKIP_PROCESS_SYMBOLS 0
#define HIDEBUG_SKIP_MATCH_SYMBOLS 0
#define HIDEBUG_SKIP_LOAD_KERNEL_SYMBOLS 0
#define HIDEBUG_SKIP_SAVE_SYMBOLS 0
#define USE_COLLECT_SYMBOLIC
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include "perf_event_record.h"
#include "perf_events.h"
#include "perf_file_writer.h"
#include "subcommand.h"
#include "virtual_runtime.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class SubCommandRecord : public SubCommand {
public:
static constexpr int DEFAULT_CPU_PERCENT = 25;
static constexpr int MIN_CPU_PERCENT = 1;
static constexpr int MAX_CPU_PERCENT = 100;
static constexpr int MIN_SAMPLE_FREQUENCY = 1;
static constexpr int MAX_SAMPLE_FREQUENCY = 100000;
static constexpr int DEFAULT_MMAP_PAGES = 256;
static constexpr int MIN_PERF_MMAP_PAGE = 2;
static constexpr int MAX_PERF_MMAP_PAGE = 1024;
static constexpr float MIN_STOP_SECONDS = 0.100;
static constexpr float MAX_STOP_SECONDS = 10000.0;
SubCommandRecord()
// clang-format off
: SubCommand("record", "Collect performance sample information",
"Usage: hiperf record [options] [command [command-args]]\n"
" Collect performance sampling information of running [command].\n"
" The default options are: -c <all cpu> --cpu-limit 25 -d 10000.0 -e hw-cpu-cycles\n"
" -f 4000 -m 1024 -o /data/local/tmp/perf.data.\n"
" -a\n"
" Collect system-wide information.\n"
" for measures all processes/threads\n"
" This requires CAP_PERFMON (since Linux 5.8) or CAP_SYS_ADMIN capability or a\n"
" /proc/sys/kernel/perf_event_paranoid value of less than 1.\n"
" --exclude-hiperf\n"
" Don't record events issued by hiperf itself.\n"
" -c <cpuid>[<,cpuid>]...\n"
" cpuid shoule be 0,1,2...\n"
" Limit the CPU that collects data.\n"
" 0 means cpu0, 1 means cpu1 ...\n"
" --cpu-limit <percent>\n"
" Set the max percent of cpu time used for recording.\n"
" percent is in range [1-100], default is 25.\n"
" -d <sec>\n"
" stop in <sec> seconds. floating point number. seconds is in range [0.100-10000.0]\n"
" defalut is 10000.0\n"
" -f <freq>\n"
" Set event sampling frequency. default is 4000 samples every second.\n"
" check /proc/sys/kernel/perf_event_max_sample_rate for maximum allowed frequency\n"
" --period <num>\n"
" Set event sampling period for tracepoint events. recording one sample when <num> events happen.\n"
" The default <num> is 1\n"
" -e <event1[:<u|k>]>[,event1[:<u|k>]]...\n"
" Customize the name of the event that needs to be sampled.\n"
" The name can use the names listed in the list parameter.\n"
" It can also be represented by the value of 0x<hex>.\n"
" u - monitor user space events only\n"
" k - monitor kernel space events only\n"
" -g <event1[:<u|k>]>[,event1[:<u|k>]]...\n"
" Put the events into a group, can set multiple groups by multiple -g\n"
" PMU is required to report data in designated groups\n"
" limited by HW capability, too many events cannot be reported in the same sampling)\n"
" --no-inherit\n"
" Don't trace child processes.\n"
" -p <pid1>[,pid2]...\n"
" Limit the process id of the collection target. Conflicts with the -a option.\n"
" -t <tid1>[,tid2]...\n"
" Limit the thread id of the collection target. Conflicts with the -a option.\n"
" --offcpu\n"
" Trace when threads are scheduled off cpu.\n"
" -j <branch_filter1>[,branch_filter2]...\n"
" taken branch stack sampling, filter can be:\n"
" any: any type of branch\n"
" any_call: any function call or system call\n"
" any_ret: any function return or system call return\n"
" ind_call: any indirect branch\n"
" call: direct calls, including far (to/from kernel) calls\n"
" u: only when the branch target is at the user level\n"
" k: only when the branch target is in the kernel\n"
" requires at least one of any, any_call, any_ret, ind_call\n"
" -s / --call-stack <fp|dwarf[,size]>\n"
" Setup and enable call stack (stack chain/backtrace) recording, Default is 'fp'.\n"
" the value can be:\n"
" fp: frame pointer\n"
" dwarf: DWARF's CFI - Call Frame Information\n"
" 'dwarf,size' set sample stack size, size should be in 8~65528 and 8 byte aligned. \n"
" as the method to collect the information used to show the call stacks.\n"
" --delay-unwind\n"
" If '-s dwarf' used, stack will be unwind while recording, use this option to switch\n"
" to unwind after recording.\n"
" --disable-unwind\n"
" If '-s dwarf' is used, stack will be unwind while recording by default\n"
" use this option to disable unwinding.\n"
" --disable-callstack-expend\n"
" If '-s dwarf' is used, to break the 64k stack limit, callstack is merged by default\n"
" to build more complete call stack. that may not be correct sometimes.\n"
" --clockid <clock_id>\n"
" Set the clock id to use for the various time fields in the perf_event_type records.\n"
" monotonic and monotonic_raw are supported,\n"
" some events might also allow boottime, realtime and clock_tai.\n"
" --symbol-dir <dir>\n"
" Set directory to look for symbol files, used for unwinding. \n"
" -m <mmap_pages>\n"
" Number of the mmap pages, used to receiving record data from kernel,\n"
" must be a power of two, rang[2,1024], default is 1024.\n"
" --app <package_name>\n"
" Collect profile info for an OHOS app, the app must be debuggable.\n"
" Record will exit if the process is not started within 10 seconds.\n"
" --data-limit <SIZE[K|M|G]>\n"
" Stop recording after SIZE bytes of records. Default is unlimited.\n"
" -o <output_file_name>\n"
" Set output file name, default is /data/local/tmp/perf.data.\n"
" -z\n"
" Compress record data.\n"
" --verbose\n"
" Show more detailed reports.\n"
" --control <command>\n"
" Control sampling by <command>, the <command> can be:\n"
" prepare: set arguments and prepare sampling\n"
" start: start sampling\n"
" pause: pause sampling\n"
" resume: resume sampling\n"
" stop: stop sampling\n"
)
// clang-format on
{
}
~SubCommandRecord();
bool OnSubCommand(std::vector<std::string> &args) override;
bool ParseOption(std::vector<std::string> &args) override;
void DumpOptions(void) const override;
static bool RegisterSubCommandRecord(void);
private:
PerfEvents perfEvents_;
bool targetSystemWide_ = false;
bool compressData_ = false;
bool noInherit_ = false;
bool excludeHiperf_ = false;
bool offCPU_ = false;
bool delayUnwind_ = false;
bool disableUnwind_ = false;
bool disableCallstackExpend_ = false;
bool verboseReport_ = false;
float timeStopSec_ = PerfEvents::DEFAULT_TIMEOUT;
int frequency_ = 0;
int period_ = 0;
int cpuPercent_ = DEFAULT_CPU_PERCENT;
int mmapPages_ = MAX_PERF_MMAP_PAGE;
std::vector<std::string> symbolDir_ = {};
std::string outputFilename_ = "/data/local/tmp/perf.data";
std::string appPackage_ = {};
std::string clockId_ = {};
std::string strLimit_ = {};
std::vector<pid_t> selectCpus_ = {};
std::vector<pid_t> selectPids_ = {};
std::vector<pid_t> selectTids_ = {};
std::vector<std::string> selectEvents_ = {};
std::vector<std::vector<std::string>> selectGroups_ = {};
std::vector<std::string> sampleTypes_ = {};
std::vector<std::string> vecBranchFilters_ = {};
std::vector<std::string> trackedCommand_ = {};
bool GetOptions(std::vector<std::string> &args);
bool CheckOptions();
bool CheckDataLimitOption();
bool CheckSelectCpuPidOption();
bool GetOptionFrequencyAndPeriod(std::vector<std::string> &args);
bool dwarfCallchainSample_ = false;
bool fpCallchainSample_ = false;
uint32_t dwarfSampleStackSize_ = MAX_SAMPLE_STACK_SIZE;
uint64_t branchSampleType_ = 0;
uint64_t dataSizeLimit_ = 0;
bool isDataSizeLimitStop_ = false;
std::unique_ptr<PerfFileWriter> fileWriter_ = nullptr;
// for client
int clientPipeInput_ = -1;
int clientPipeOutput_ = -1;
std::thread clientCommandHanle_;
bool clientExit_ = false;
void ClientCommandHandle();
bool ClientCommandResponse(bool OK);
bool IsSamplingRunning();
// for cmdline client
std::string controlCmd_ = {};
bool isFifoServer_ = false;
bool isFifoClient_ = false;
bool ProcessControl();
bool CreateFifoServer();
bool SendFifoAndWaitReply(const std::string &cmd);
bool WaitFifoReply(int fd);
void CloseClientThread();
// just a debug test function
bool AddSelectEvent(std::vector<std::string> &events, bool groups = false);
enum EventSpaceType {
NONE = 0,
USER = 1,
KERNEL = 2,
USER_KERNEL = 3,
};
uint8_t eventSpaceType_ = EventSpaceType::NONE;
PerfEventParanoid request_ = PerfEventParanoid::USER;
bool ParseEventList(std::vector<std::string> &list);
bool ParseGroupList(std::vector<std::vector<std::string>> &list);
bool PreparePerfEvent();
bool PrepareSys();
bool PrepareVR();
size_t recordSamples_ = 0;
size_t recordNoSamples_ = 0;
// callback to process record
bool ProcessRecord(std::unique_ptr<PerfEventRecord>);
bool SaveRecord(std::unique_ptr<PerfEventRecord>);
// file format like as 0,1-3,4-6,7,8
int GetCountFromFile(const std::string &fileName);
std::string GetCpuDescFromFile();
bool AddCpuFeature();
void AddMemTotalFeature();
void AddEventDescFeature();
void AddRecordTimeFeature();
void AddWorkloadCmdFeature();
void AddCommandLineFeature();
void AddCpuOffFeature();
bool AddFeatureRecordFile();
bool CreateInitRecordFile(bool compressData = false);
bool FinishWriteRecordFile();
bool PostProcessRecordFile();
bool RecordCompleted();
#ifdef HIPERF_DEBUG_TIME
void ReportTime();
#endif
bool CollectionSymbol(std::unique_ptr<PerfEventRecord> record);
bool SetPerfCpuMaxPercent();
bool SetPerfMaxSampleRate();
bool TraceOffCpu();
bool ParseCallStackOption(const std::vector<std::string> &vecSampleTypes);
bool ParseDataLimitOption(const std::string &str);
bool ParseBranchSampleType(const std::vector<std::string> &vecBranchSampleTypes);
bool ParseControlCmd(const std::string cmd);
bool CheckTargetProcessOptions();
bool CheckTargetPids();
pid_t GetAppPackagePid(const std::string &appPackge);
VirtualRuntime virtualRuntime_;
#ifdef USE_COLLECT_SYMBOLIC
std::unordered_set<uint64_t> kernelSymbolsHits_;
std::unordered_map<pid_t, std::unordered_set<uint64_t>> userSymbolsHits_;
void SymbolicHits();
#endif
#ifdef HIPERF_DEBUG_TIME
std::chrono::microseconds prcessRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds saveRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds saveFeatureTimes_ = std::chrono::microseconds::zero();
#endif
std::chrono::time_point<std::chrono::steady_clock> startSaveFileTimes_;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

191
include/subcommand_report.h Normal file
View File

@ -0,0 +1,191 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef SUBCOMMAND_REPORT_H
#define SUBCOMMAND_REPORT_H
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <linux/perf_event.h>
#include <map>
#include <optional>
#include <set>
#include <stdio.h>
#include <tuple>
#include "perf_file_reader.h"
#if HAVE_PROTOBUF
#include "report_protobuf_file.h"
#endif
#include "debug_logger.h"
#include "option.h"
#include "perf_event_record.h"
#include "report.h"
#include "report_json_file.h"
#include "subcommand.h"
#include "symbols_file.h"
#include "utilities.h"
#include "virtual_runtime.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class SubCommandReport : public SubCommand {
public:
SubCommandReport()
// clang-format off
: SubCommand("report", "report sampling information from perf.data format file",
"Usage: hiperf report [options]\n"
" The default options are same as :\n"
" -i perf.data --sort comm,pid,tid,dso,func\n"
" --symbol-dir <dir>\n"
" use symbols path to find symbols.\n"
" separate the paths with commas.\n"
" --limit-percent <number>\n"
" only show heat percent limit content.\n"
" -s / --call-stack\n"
" show the unwind callstack\n"
" --call-stack-limit-percent <number>\n"
" only show the callstack heat percent limit content.\n"
" --proto\n"
" show protobuf content in the save file name.\n"
" default file name is perf.data.\n"
" --json\n"
" report in json format.\n"
" default file name is perf.data.\n"
" --diff <target file>\n"
" show the diff result from -i to -diff .\n"
" exapmle: \"report -i a.data --diff b.data\"\n"
" --branch\n"
" show the branch from address instead of ip adress\n"
" --<keys> <keyname1>[,keyname2][,...]\n"
" select able keys: comms,pids,tids,dsos,funcs,from_dsos,from_funcs\n"
" example: --comms hiperf\n"
" --sort <key1>[,key2][,...]\n"
" Choose some keywords.\n"
" These keywords will be used for sorting.\n"
" Only selected keywords will appear in the report.\n"
"\n"
" pid -- process id\n"
" tid -- thread id\n"
" comm -- thread name (can be changed for same thread)\n"
" dso -- like dso name , or elf path\n"
" func -- function name in the symbols\n"
"\n"
" in branch mode:\n"
" from_dso -- branched from dso or elf\n"
" from_func -- branched from function\n"
" dso -- means branched to dso or elf\n"
" func -- means branched to function\n"
" -i <filename>\n"
" perf data file to report, default is perf.data\n"
" -o <filename>\n"
" report file name. if empty will use stdout print\n"
" --hide_count\n"
" will not show count in report\n"
"\n"
), recordFile_ {"perf.data", ""} // dafault file path is perf.data
// clang-format on
{
}
bool OnSubCommand(std::vector<std::string> &args) override;
bool ParseOption(std::vector<std::string> &args) override;
void DumpOptions(void) const override;
static bool RegisterSubCommandReport(void);
bool RecordCallBack(std::unique_ptr<PerfEventRecord> record);
~SubCommandReport();
private:
void ProcessSample(std::unique_ptr<PerfRecordSample> &);
void BroadcastSample(std::unique_ptr<PerfRecordSample> &);
bool VerifyOption();
bool VerifyDisplayOption();
// load
bool PrepareOutput();
bool LoadPerfData();
void ProcessFeaturesData();
void LoadEventConfigData();
void LoadAttrSection();
void LoadEventDesc();
void ProcessSymbolsData();
void LoadPerfDataCompleted();
bool OutputReport();
bool OutputStd();
// prefdata
bool showCallStack_ = false;
bool diffMode_ = false;
// create record file reader pointer
std::unique_ptr<PerfFileReader> recordFileReader_;
std::vector<std::string> symbolsPaths_;
// report file name , if empty will use stdout
std::string reportFile_;
// for diff report
enum RecordIndex { FIRST = 0, SECOND = 1, MAX = 2, CURRENT = -1 } index_ = FIRST;
std::string recordFile_[MAX];
ReportOption reportOption_;
Report report_[MAX] = {reportOption_, reportOption_};
inline Report &GetReport(RecordIndex index = CURRENT, int configId = 0)
{
if (index == CURRENT) {
return report_[index_];
} else {
return report_[index];
}
}
std::vector<std::string> configNames_;
std::set<uint64_t> cpuOffids_;
const std::string cpuOffEventName = "sched:sched_switch";
bool cpuOffMode_ = false;
std::map<pid_t, std::unique_ptr<PerfRecordSample>> prevSampleCache_;
void FlushCacheRecord();
// in debug mode we will output some more debug info
bool debug_ = false;
bool branch_ = false;
bool jsonFormat_ = false;
FILE *output_ = nullptr;
std::unique_ptr<ReportJsonFile> reportJsonFile_ = nullptr;
bool protobufFormat_ = false;
#if HAVE_PROTOBUF
std::unique_ptr<ReportProtobufFileWriter> protobufOutputFileWriter_ = nullptr;
void UpdateReportInfo();
#endif
friend class SubCommandReportTest;
FRIEND_TEST(SubCommandReportTest, TestLoadPerfData);
FRIEND_TEST(SubCommandReportTest, TestOutputReport);
FRIEND_TEST(SubCommandReportTest, TestOutputStd);
FRIEND_TEST(SubCommandReportTest, TestVerifyOption);
FRIEND_TEST(SubCommandReportTest, TestVerifyDisplayOption);
FRIEND_TEST(SubCommandReportTest, TestPrepareConsole);
FRIEND_TEST(SubCommandReportTest, TestPrepareConsole);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

124
include/subcommand_stat.h Normal file
View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef SUBCOMMAND_STAT_H_
#define SUBCOMMAND_STAT_H_
#include "option.h"
#include "perf_events.h"
#include "subcommand.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class SubCommandStat : public SubCommand {
public:
SubCommandStat()
: SubCommand("stat", "Collect performance counter information",
// clang-format off
"Usage: hiperf stat [options] [command [command-args]]\n"
" Collect performance counter information of running [command].\n"
" The default options are: -c -1 -d 10000.0\n"
" -a\n"
" Collect system-wide information.\n"
" for measures all processes/threads\n"
" This requires CAP_PERFMON (since Linux 5.8) or\n"
" CAP_SYS_ADMIN capability or a\n"
" /proc/sys/kernel/perf_event_paranoid value of less than 1.\n"
" -c <cpuid>[<,cpuid>]\n"
" cpuid shoule be 0,1,2...\n"
" Limit the CPU that collects data.\n"
" 0 means cpu0, 1 means cpu1 ...\n"
" -d <sec>\n"
" stop in <sec> seconds.\n"
" floating point number.\n"
" defalut is 10000.0\n"
" -i <ms>\n"
" print stat info every <ms>.\n"
" -e event1[:<u|k>][,event1[:<u|k>]]...\n"
" Customize the name of the event that needs to be counted.\n"
" The name can use the names listed in the list parameter.\n"
" It can also be represented by the value of 0x<hex>.\n"
" u - monitor user space events only\n"
" k - monitor kernel space events only\n"
" -g <event1[:<u|k>]>[,event1[:<u|k>]]...\n"
" The grouping function is added on the basis of the function of the -e parameter\n"
" PMU is required to report data in designated groups\n"
" limited by HW capability, too many events cannot be reported in the same sampling)\n"
" --no-inherit\n"
" Don't track new processes/threads.\n"
" -p <pid1>[,pid2]...\n"
" Limit the process id of the collection target. Conflicts with the -a option.\n"
" -t <tid1>[,tid2]...\n"
" Limit the thread id of the collection target. Conflicts with the -a option.\n"
" --verbose\n"
" Show more detailed reports.\n"
// clang-format on
),
targetSystemWide_(false)
{
}
bool OnSubCommand(std::vector<std::string> &args) override;
bool ParseOption(std::vector<std::string> &args) override;
void DumpOptions(void) const override;
private:
PerfEvents perfEvents_;
bool targetSystemWide_ {false};
std::vector<int> selectCpus_ = {};
float timeStopSec_ = PerfEvents::DEFAULT_TIMEOUT;
int timeReportMs_ {0};
std::vector<std::vector<std::string>> selectEvents_;
std::vector<std::vector<std::string>> selectGroups_;
bool noCreateNew_ {false};
std::vector<pid_t> selectPids_;
std::vector<pid_t> selectTids_;
bool verboseReport_ {false};
std::vector<std::string> trackedCommand_ {};
bool helpOption_ {false};
PerfEventParanoid request_ = USER;
bool CheckOptionPid(std::vector<pid_t> pids);
static __u64 FindEventCount(
const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
const std::string &configName, const __u64 group_id, double &scale);
static void GetComments(
const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
std::map<std::string, std::string> &comments);
static bool FindRunningTime(
const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
double &running_time_in_sec, __u64 &group_id, double &main_scale);
static bool IsMonitoredAtAllTime(const double &scale);
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>> &);
void PrintUsage();
inline bool HelpOption()
{
return helpOption_;
}
bool PrepairEvents();
bool CheckOptions(const std::vector<pid_t> &pids);
bool CheckSelectCpuPidOption();
bool ParseEventList(std::vector<std::string> &list);
bool ParseGroupList(std::vector<std::vector<std::string>> &list);
};
bool RegisterSubCommandStat(void);
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

345
include/symbols_file.h Executable file
View File

@ -0,0 +1,345 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_SYMBOLS_H
#define HIPERF_SYMBOLS_H
#include <cinttypes>
#include <iomanip>
#include <sstream>
#include <string>
#include "perf_file_format.h"
#include "utilities.h"
#define HIPERF_ELF_READ_USE_MMAP
namespace OHOS {
namespace Developtools {
namespace HiPerf {
constexpr const char KERNEL_MMAP_NAME[] = "[kernel.kallsyms]";
constexpr const char KERNEL_MODULES_EXT_NAME[] = ".ko";
constexpr const char KERNEL_ELF_NAME[] = "vmlinux";
constexpr const char MMAP_VDSO_NAME[] = "[vdso]";
constexpr const char MMAP_ANONYMOUS_NAME[] = "[anon]";
constexpr const char MMAP_ANONYMOUS_OHOS_NAME[] = "//anon";
const std::string NOTE_GNU_BUILD_ID = ".note.gnu.build-id";
const std::string EH_FRAME_HR = ".eh_frame_hdr";
const std::string EH_FRAME = ".eh_frame";
const std::string ARM_EXIDX = ".ARM.exidx";
const std::string SYMTAB = ".symtab";
const std::string DYNSYM = ".dynsym";
const std::string GNU_DEBUGDATA = ".gnu_debugdata";
const std::string PLT = ".plt";
const std::string LINKER_PREFIX = "__dl_";
const std::string LINKER_PREFIX_NAME = "[linker]";
const int MAX_SYMBOLS_TYPE_NAME_LEN = 10;
class FileSymbol {
[[maybe_unused]] uint64_t vaddr_ = 0;
[[maybe_unused]] uint64_t len_ = 0;
std::string name_ = "";
std::string demangle_ = ""; // demangle string
FileSymbol(uint64_t vaddr, uint64_t len, const char *name, const char *demangle)
: vaddr_(vaddr), len_(len), name_(name), demangle_(demangle)
{
}
};
struct Symbol {
uint64_t funcVaddr_ = 0;
uint64_t fileVaddr_ = 0;
uint64_t taskVaddr_ = 0;
uint64_t len_ = 0;
int32_t index_ = -1;
std::string_view name_ = "";
std::string_view demangle_ = ""; // demangle string
std::string_view module_ = ""; // maybe empty
std::string_view comm_ = ""; // we need a comm name like comm@0x1234
mutable std::string_view unknow_ = "";
mutable bool matched_ = false; // if some callstack match this
int32_t hit_ = 0;
// elf use this
Symbol(uint64_t vaddr, uint64_t len, const std::string &name, const std::string &demangle,
const std::string module)
: funcVaddr_(vaddr),
fileVaddr_(vaddr),
len_(len),
name_(memHolder.HoldStringView(name)),
demangle_(memHolder.HoldStringView(demangle)),
module_(memHolder.HoldStringView(module)) {};
Symbol(uint64_t vaddr, uint64_t len, const std::string &name, const std::string &module)
: Symbol(vaddr, len, name, name, module) {};
// kernel use this
Symbol(uint64_t vaddr, const std::string &name, const std::string &module)
: Symbol(vaddr, 0, name, name, module) {};
// Symbolic use this
Symbol(uint64_t taskVaddr = 0, const std::string &comm = "")
: taskVaddr_(taskVaddr), comm_(comm) {};
static bool SameVaddr(const Symbol &a, const Symbol &b)
{
return (a.funcVaddr_ == b.funcVaddr_);
}
bool Same(const Symbol &b) const
{
return (funcVaddr_ == b.funcVaddr_ and demangle_ == b.demangle_);
}
bool operator==(const Symbol &b) const
{
return Same(b);
}
bool operator!=(const Symbol &b) const
{
return !Same(b);
}
bool isValid() const
{
return !module_.empty();
}
void SetMatchFlag() const
{
matched_ = true;
}
inline bool HasMatched() const
{
return matched_;
}
std::string_view Name() const
{
if (!demangle_.empty()) {
return demangle_;
}
if (!name_.empty()) {
return name_;
}
if (unknow_.empty()) {
std::stringstream sstream;
if (!module_.empty()) {
sstream << module_ << "+0x" << std::hex << fileVaddr_;
} else {
sstream << comm_ << "@0x" << std::hex << taskVaddr_;
}
unknow_ = memHolder.HoldStringView(sstream.str());
}
return unknow_;
}
std::string ToString() const
{
std::stringstream sstream;
if (fileVaddr_ != 0) {
sstream << "0x" << std::hex << fileVaddr_;
} else {
sstream << "0x" << std::hex << taskVaddr_;
}
sstream << " " << Name();
return sstream.str();
};
std::string ToDebugString() const
{
std::stringstream sstream;
sstream << "0x" << std::setfill('0') << std::setw(sizeof(funcVaddr_) * BYTE_PRINT_WIDTH)
<< std::hex << funcVaddr_;
sstream << "|";
sstream << std::setfill('0') << std::setw(sizeof(len_)) << len_;
sstream << "|";
sstream << demangle_ << "|";
sstream << name_ << "|";
sstream << (matched_ ? "matched" : "");
return sstream.str();
};
bool Contain(uint64_t addr) const
{
if (len_ == 0) {
return funcVaddr_ <= addr;
} else {
return (funcVaddr_ <= addr) and ((funcVaddr_ + len_) > addr);
}
}
// The range [first, last) must be partitioned with respect to the expression !(value < element)
// or !comp(value, element)
static bool ValueLessThen(uint64_t vaddr, const Symbol &a)
{
return vaddr < a.funcVaddr_;
}
static bool ValueLessEqual(uint64_t vaddr, const Symbol &a)
{
return vaddr <= a.funcVaddr_;
}
static bool CompareLessThen(const Symbol &a, const Symbol &b)
{
return a.funcVaddr_ < b.funcVaddr_; // we should use vaddr to sort
};
static bool CompareByPointer(const Symbol *a, const Symbol *b)
{
return a->funcVaddr_ < b->funcVaddr_; // we should use vaddr to sort
};
};
enum SymbolsFileType {
SYMBOL_KERNEL_FILE,
SYMBOL_KERNEL_MODULE_FILE,
SYMBOL_ELF_FILE,
SYMBOL_JAVA_FILE,
SYMBOL_JS_FILE,
SYMBOL_UNKNOW_FILE,
};
class SymbolsFile {
public:
SymbolsFileType symbolFileType_;
std::string filePath_ = "";
// [14] .text PROGBITS 00000000002c5000 000c5000
// min exec addr , general it point to .text
// we make a default value for min compare
static const uint64_t maxVaddr = std::numeric_limits<uint64_t>::max();
uint64_t textExecVaddr_ = maxVaddr;
uint64_t textExecVaddrFileOffset_ = 0;
uint64_t textExecVaddrRange_ = maxVaddr;
SymbolsFile(SymbolsFileType symbolType, const std::string path)
: symbolFileType_(symbolType), filePath_(path) {};
virtual ~SymbolsFile();
// create the symbols file object
static std::unique_ptr<SymbolsFile> CreateSymbolsFile(
SymbolsFileType = SYMBOL_UNKNOW_FILE, const std::string symbolFilePath = EMPTY_STRING);
static std::unique_ptr<SymbolsFile> CreateSymbolsFile(const std::string &symbolFilePath);
// set symbols path
bool setSymbolsFilePath(const std::string &symbolsSearchPath)
{
std::vector<std::string> symbolsSearchPaths = {symbolsSearchPath};
return setSymbolsFilePath(symbolsSearchPaths);
};
bool setSymbolsFilePath(const std::vector<std::string> &);
// load symbol from file
virtual bool LoadSymbols([[maybe_unused]] const std::string &symbolFilePath = EMPTY_STRING)
{
HLOGV("virtual dummy function called");
symbolsLoaded_ = true;
return false;
};
// load debug infor for unwind
virtual bool LoadDebugInfo([[maybe_unused]] const std::string &symbolFilePath = EMPTY_STRING)
{
HLOGV("virtual dummy function called");
debugInfoLoaded_ = true;
return false;
};
// get the build if from symbols
const std::string GetBuildId() const;
// get the symbols vector
const std::vector<Symbol> &GetSymbols();
const std::vector<Symbol *> &GetMatchedSymbols();
// get vaddr(in symbol) from ip(real addr , after mmap reloc)
virtual uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const;
// get symbols from vaddr
const Symbol GetSymbolWithVaddr(uint64_t vaddr);
// read the .text section and .eh_frame section (RO) memory from elf mmap
// unwind use this to check the DWARF and so on
virtual size_t ReadRoMemory(uint64_t, uint8_t * const, size_t) const
{
HLOGV("virtual dummy function called");
return 0; // default not support
}
// get the section info , like .ARM.exidx
virtual bool GetSectionInfo([[maybe_unused]] const std::string &name,
[[maybe_unused]] uint64_t &sectionVaddr,
[[maybe_unused]] uint64_t &sectionSize,
[[maybe_unused]] uint64_t &sectionFileOffset) const
{
HLOGV("virtual dummy function called");
return false;
}
#ifndef __arm__
// get hdr info for unwind , need provide the fde table location and entry count
virtual bool GetHDRSectionInfo([[maybe_unused]] uint64_t &ehFrameHdrElfOffset,
[[maybe_unused]] uint64_t &fdeTableElfOffset,
[[maybe_unused]] uint64_t &fdeTableSize) const
{
HLOGV("virtual dummy function called");
return false;
}
#endif
// load from symbols from the perf.data format
static std::unique_ptr<SymbolsFile> LoadSymbolsFromSaved(const SymbolFileStruct &);
// save the symbols to perf.data format
void ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct);
bool SymbolsLoaded()
{
return symbolsLoaded_;
}
// this means we are in recording
// will try read some elf in runtime path
static bool onRecording_;
protected:
bool symbolsLoaded_ = false;
bool debugInfoLoaded_ = false;
const std::string FindSymbolFile(const std::vector<std::string> &,
std::string symboleFilePath = EMPTY_STRING) const;
std::string SearchReadableFile(const std::vector<std::string> &searchPaths,
const std::string &filePath) const;
bool UpdateBuildIdIfMatch(std::string buildId);
std::string buildId_;
std::vector<std::string> symbolsFileSearchPaths_;
std::vector<Symbol> symbols_ {};
std::vector<Symbol *> matchedSymbols_ {};
std::vector<FileSymbol> fileSymbols_ {};
void AdjustSymbols();
void SortMatchedSymbols();
bool CheckPathReadable(const std::string &path) const;
FRIEND_TEST(SymbolsFileTest, FindSymbolFile);
FRIEND_TEST(SymbolsFileTest, UpdateBuildIdIfMatch);
FRIEND_TEST(SymbolsFileTest, exportSymbolToFileFormat);
FRIEND_TEST(SymbolsFileTest, exportSymbolToFileFormatMatched);
friend class VirtualRuntimeTest;
FRIEND_TEST(ReportJsonFileTest, ProcessSymbolsFiles);
FRIEND_TEST(ReportProtobufFileTest, ProcessSymbolsFiles);
friend class ReportProtobufFileTest;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

80
include/tracked_command.h Executable file
View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_TRACKED_COMMAND_H_
#define HIPERF_TRACKED_COMMAND_H_
#include <memory>
#include <string>
#include <unistd.h>
#include <vector>
#include "noncopyable.h"
#include <utilities.h>
namespace OHOS {
namespace Developtools {
namespace HiPerf {
class TrackedCommand : public Noncopyable {
public:
enum class State {
COMMAND_WAITING, // child process blocked to execute command
COMMAND_STARTED, // child process executing command
COMMAND_FAILURE, // command failed to start
COMMAND_STOPPED // no child process or command execution
};
static std::unique_ptr<TrackedCommand> CreateInstance(const std::vector<std::string> &args);
~TrackedCommand();
bool CreateChildProcess();
bool StartCommand();
bool WaitCommand(int &wstatus);
void Stop();
inline std::string GetCommandName()
{
if (!command_.empty()) {
return command_[0];
}
return EMPTY_STRING;
}
inline State GetState()
{
return state_;
}
inline pid_t GetChildPid()
{
return childPid_;
}
private:
explicit TrackedCommand(const std::vector<std::string> &args);
bool InitSignalPipes(int &startFd, int &ackFd);
void ExecuteCommand(const int &startFd, const int &ackFd);
void MakeInvalid();
std::vector<std::string> command_ {};
int startFd_ {-1};
int ackFd_ {-1};
pid_t childPid_ {-1};
State state_ {State::COMMAND_STOPPED};
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

321
include/utilities.h Executable file
View File

@ -0,0 +1,321 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_UTILITIES_H_
#define HIPERF_UTILITIES_H_
// for security function
#include <securec.h>
#include <algorithm>
#include <cctype>
#include <cinttypes>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <dirent.h>
#include <fcntl.h>
#include <file_ex.h>
#include <stddef.h>
#include <sys/stat.h>
#include <unique_fd.h>
#include <unistd.h>
#if !is_mingw
#include <gtest/gtest_prod.h>
#include <sys/syscall.h>
#endif
#include <linux/types.h>
#include "debug_logger.h"
#include "noncopyable.h"
// data and value
/*
long long always 64 only in ILP64, int is 64 otherwise int is always 32
*/
using s8 = __s8;
using u8 = __u8;
using s16 = __s16;
using u16 = __u16;
using s32 = __s32;
using u32 = __u32;
using s64 = __s64;
using u64 = __u64;
constexpr const int NUMBER_FORMAT_HEX_BASE = 16;
constexpr const int BYTE_PRINT_WIDTH = 2;
constexpr const int UINT64_PRINT_WIDTH = BYTE_PRINT_WIDTH * 8;
constexpr const int BITS_OF_BYTE = 8;
constexpr const int BITS_OF_TWO_BYTE = 2 * BITS_OF_BYTE;
constexpr const int BITS_OF_FOUR_BYTE = 4 * BITS_OF_BYTE;
constexpr const int FULL_PERCENTAGE = 100;
constexpr const int FULL_PERCENTAGE_NUM_LEN = 5; // 100.00
constexpr const int FULL_PERCENTAGE_DIFF_NUM_LEN = 6; // +100.00
constexpr const int FULL_PERCENTAGE_LEN = 6; // 100.00%
constexpr const int FULL_PERCENTAGE_DIFF_LEN = 7; // +100.00%
constexpr const int THOUSANDS = 1000;
constexpr const int HUNDREDS = 100;
constexpr const int DEFAULT_STRING_BUF_SIZE = 4096;
constexpr const int FIVE_THOUSANDS = 5000;
#if !is_mingw
#ifndef O_BINARY
#define O_BINARY 0
#endif
#endif
constexpr const double MS_DUARTION =
static_cast<double>(std::chrono::milliseconds::duration::period::den);
constexpr uint64_t KILO = 1024;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
const std::string EMPTY_STRING = "";
// string function
static class StringViewMemoryHold {
public:
~StringViewMemoryHold()
{
for (auto &p : holder_) {
delete[] p;
}
}
const char *HoldStringView(std::string_view view)
{
if (view.size() == 0) {
return "";
}
// for null end
char *p = new char[view.size() + 1];
p[view.size()] = '\0';
std::copy(view.data(), view.data() + view.size(), p);
holder_.emplace_back(p);
return p;
};
private:
std::vector<char *> holder_;
} memHolder;
std::string StringReplace(std::string source, const std::string &from, const std::string &to);
template<class T>
std::string VectorToString(const std::vector<T> &items)
{
if constexpr (std::is_same<T, std::vector<std::string>>::value) {
std::vector<std::string> stringItems;
for (auto item : items) {
stringItems.push_back("[" + VectorToString(item) + "]");
}
return VectorToString(stringItems);
} else {
std::string itemsString;
const std::string split = ",";
for (auto item : items) {
if (!itemsString.empty())
itemsString.append(split);
if constexpr (std::is_same<T, std::string>::value) {
itemsString.append(item);
} else {
itemsString.append(std::to_string(item));
}
}
if (itemsString.empty())
itemsString.append("<empty>");
return itemsString;
}
}
std::string BufferToHexString(const std::vector<unsigned char> &vec);
std::string BufferToHexString(const unsigned char buf[], size_t size);
void HexDump(const void *buf, size_t size, size_t max_size = 0);
std::string &StringTrim(std::string &s);
std::vector<std::string> StringSplit(std::string source, std::string split = ",");
size_t SubStringCount(const std::string &source, const std::string &sub);
bool StringStartsWith(const std::string &string, const std::string &with);
bool StringEndsWith(const std::string &string, const std::string &with);
bool IsSameCommand(std::string cmdLine, std::string cmdName);
std::vector<pid_t> GetSubthreadIDs(const pid_t pid);
bool IsDigits(const std::string &str);
bool IsHexDigits(const std::string &str);
constexpr const int COMPRESS_READ_BUF_SIZE = 4096;
// compress specified dataFile into gzip file
bool CompressFile(const std::string &dataFile, const std::string &destFile);
// uncompress specified gzip file into dataFile
bool UncompressFile(const std::string &gzipFile, const std::string &dataFile);
template<typename... VA>
std::string StringPrintf(const char *stringFormat, VA... args)
{
// check howmany bytes we need
char bytes[DEFAULT_STRING_BUF_SIZE];
bytes[DEFAULT_STRING_BUF_SIZE - 1] = '\0';
if (stringFormat == nullptr) {
return EMPTY_STRING;
}
// print it to bytes
if (snprintf_s(bytes, DEFAULT_STRING_BUF_SIZE, DEFAULT_STRING_BUF_SIZE - 1, stringFormat,
args...) < 0) {
return EMPTY_STRING;
}
// make a string return
return std::string(bytes);
}
// path check
std::vector<std::string> GetEntriesInDir(const std::string &basePath);
std::vector<std::string> GetSubDirs(const std::string &basePath);
bool IsDir(const std::string &path);
bool IsPath(const std::string &fileName);
#if is_mingw
const char PATH_SEPARATOR = '\\';
#else
const char PATH_SEPARATOR = '/';
#endif
const std::string PATH_SEPARATOR_STR = std::string(1, PATH_SEPARATOR);
std::string PlatformPathConvert(const std::string &path);
// attribute
#define PACKED __attribute__((packed))
// data align
// some time u will meet signal 7 (SIGBUS), code 1 (BUS_ADRALN) in 32 or 64 arch cpu
#define HIPERF_BUF_ALIGN alignas(64)
uint32_t RoundUp(uint32_t x, const int align);
// data convert funcion
template<class T>
std::string ToHex(const T &source, int size = sizeof(T), bool perfix = false)
{
std::stringstream ss;
if (perfix) {
ss << "0x";
}
ss << std::hex << std::setw(BYTE_PRINT_WIDTH * size) << std::setfill('0') << (uint64_t)source;
return ss.str();
}
// data move and copy
template<class S, class T>
size_t inline CopyFromBufferAndMove(S *&buffer, T *dest, size_t size = 0)
{
if (size == 0) {
size = sizeof(T);
}
if (memcpy_s(dest, size, buffer, size) != EOK) {
return size;
}
buffer = buffer + size;
return size;
}
// file read write
bool ReadIntFromProcFile(const std::string &path, int &value);
bool WriteIntToProcFile(const std::string &path, int value);
std::string ReadFileToString(const std::string &fileName);
bool ReadFileToString(const std::string &fileName, std::string &content, size_t fileSize = 0);
bool WriteStringToFile(const std::string &fileName, const std::string &value);
// stdout
class StdoutRecord {
public:
~StdoutRecord()
{
Stop(); // stdout need restore
}
StdoutRecord(const std::string &tempFile = EMPTY_STRING,
const std::string &mode = EMPTY_STRING);
bool Start();
std::string Stop();
private:
OHOS::UniqueFd stdoutFile_; // back and restore stdout
std::FILE *recordFile_ = nullptr; // save the output
bool stop_ = true;
std::string content_ = EMPTY_STRING;
};
// misc
template<class T>
float Percentage(const T &a, const T &b)
{
return static_cast<float>(a) / static_cast<float>(b) * FULL_PERCENTAGE;
}
bool IsRoot();
bool PowerOfTwo(int n);
#define INDENT_ONE_LEVEL (indent + 1)
#define INDENT_TWO_LEVEL (indent + 2)
#define PrintIndent(indent, format, ...) \
if (indent >= 0) { \
printf("%*s" format, (indent)*2, "", ##__VA_ARGS__); \
} else { \
HLOGV("%s" format, "", ##__VA_ARGS__); \
}
#ifndef MMAP_FAILED
#define MMAP_FAILED reinterpret_cast<void *>(-1)
#endif
#ifndef MAP_FAILED
#define MAP_FAILED MMAP_FAILED
#endif
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
// this will also used for libunwind head (out of namespace)
#if is_mingw
#if !is_double_framework
#define HAVE_MMAP 1
#define MAP_PRIVATE 0x02
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4
void *mmap(void *addr, size_t length, int prot, int flags, int fd, size_t offset);
int munmap(void *addr, size_t);
#endif
#endif
#endif

179
include/virtual_runtime.h Executable file
View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_VIRTUAL_RUNTIME_H
#define HIPERF_VIRTUAL_RUNTIME_H
#include <functional>
#include "callstack.h"
#include "perf_event_record.h"
#include "symbols_file.h"
#include "virtual_thread.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
/*
This Class contains userspace thread objects. and kernel space objects
It represents a virtual operating environment, mainly referring to the relationship between pid,
mmaps, and symbols.
It mainly receives data is ip pointer (virtual address), pid
According to these data, it will find the corresponding mmap and its corresponding elf (also called
DSO)
Then find the corresponding symbol in the corresponding elf symbol file according to the offset
recorded in the corresponding mmap.
*/
class VirtualRuntime {
public:
VirtualRuntime(bool onDevice = true);
// thread need hook the record
// from the record , it will call back to write some Simulated Record
// case 1. some mmap will be create when it read mmaps for each new process (from record sample)
using RecordCallBack = std::function<bool(std::unique_ptr<PerfEventRecord>)>;
void SetRecordMode(RecordCallBack recordCallBack);
// this both used in report and record follow
// it process the record, and rebuild the trhread maps
// It internally determines whether to go to the Record process (which will generate virtual
// events) or the Report process by judging whether SetRecordMode has been passed.
void UpdateFromRecord(PerfEventRecord &reocrd);
// in reocrd mode
// we make a kernel symbols from some proc file
void UpdateKernelSpaceMaps();
void UpdateKernelModulesSpaceMaps();
// load vdso
void LoadVdso();
void UpdateKernelSymbols();
void UpdateKernelModulesSymbols();
// set symbols path , it will send to every symobile file for search
bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths);
// any mode
static_assert(sizeof(pid_t) == sizeof(int));
const std::vector<std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const
{
return symbolsFiles_;
}
void SetCallStackExpend(size_t mergeLevel = 0)
{
callstackMergeLevel_ = mergeLevel;
}
void SetDisableUnwind(bool disableUnwind)
{
HLOGV("disableUnwind change to %d", disableUnwind);
disableUnwind_ = disableUnwind;
}
const Symbol GetSymbol(uint64_t ip, pid_t pid, pid_t tid,
const perf_callchain_context &context = PERF_CONTEXT_MAX);
VirtualThread &GetThread(pid_t pid, pid_t tid);
const std::map<pid_t, VirtualThread> &GetThreads() const
{
return userSpaceThreadMap_;
}
void SymbolicRecord(PerfRecordSample &recordSample);
// report use
void UpdateFromPerfData(const std::vector<SymbolFileStruct> &);
void UnwindFromRecord(PerfRecordSample &recordSample);
// debug time
#ifdef HIPERF_DEBUG_TIME
std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds prcessSampleRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds prcessMmapRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds prcessMmap2RecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds prcessCommRecordTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds threadParseMapsTimes_ = std::chrono::microseconds::zero();
std::chrono::microseconds threadCreateMmapTimes_ = std::chrono::microseconds::zero();
#endif
const bool loadSymboleWhenNeeded_ = true; // thie is a feature config
private:
bool disableUnwind_ = true;
size_t callstackMergeLevel_ = 1;
CallStack callstack_;
// pid map with user space thread
std::map<pid_t, VirtualThread> userSpaceThreadMap_;
// not pid , just memmap
std::vector<MemMapItem> kernelSpaceMemMaps_;
RecordCallBack recordCallBack_;
std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles_;
enum SymbolCacheLimit : std::size_t {
KERNEL_SYMBOL_CACHE_LIMIT = 4000,
THREAD_SYMBOL_CACHE_LIMIT = 2000,
};
std::unordered_map<pid_t, HashList<uint64_t, Symbol>> threadSymbolCache_;
HashList<uint64_t, Symbol> kernelSymbolCache_ {KERNEL_SYMBOL_CACHE_LIMIT};
bool GetSymbolCache(uint64_t ip, pid_t pid, pid_t tid, Symbol &symbol,
const perf_callchain_context &context);
// find synbols function name
void MakeCallFrame(Symbol &symbol, CallFrame &callFrame);
// records
void UpdateSymbols(std::string filename);
void UpdateFromRecord(PerfRecordSample &recordSample);
void UpdateFromRecord(PerfRecordMmap &recordMmap);
void UpdateFromRecord(PerfRecordMmap2 &recordMmap2);
void UpdateFromRecord(PerfRecordComm &recordComm);
// threads
VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = "");
std::string ReadThreadName(pid_t tid);
VirtualThread &CreateThread(pid_t pid, pid_t tid);
// maps
void UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename, uint64_t begin,
uint64_t len, uint64_t offset);
void UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset, std::string filename);
const Symbol GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps,
const VirtualThread &thread);
const Symbol GetUserSymbol(uint64_t ip, const VirtualThread &thread);
#ifdef HIPERF_DEBUG
std::unordered_set<uint64_t> missedRuntimeVaddr_;
#endif
void SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
perf_callchain_context context);
std::vector<std::string> symbolsPaths_;
FRIEND_TEST(VirtualRuntimeTest, SetRecordMode);
FRIEND_TEST(VirtualRuntimeTest, UpdateKernelSymbols);
FRIEND_TEST(VirtualRuntimeTest, UpdateKernelModulesSymbols);
FRIEND_TEST(VirtualRuntimeTest, SetCallStackExpend);
FRIEND_TEST(VirtualRuntimeTest, SetDisableUnwind);
FRIEND_TEST(VirtualRuntimeTest, UnwindFromRecord);
friend class VirtualRuntimeTest;
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

113
include/virtual_thread.h Executable file
View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_VIRTUAL_THREAD_H
#define HIPERF_VIRTUAL_THREAD_H
#include <cinttypes>
#include <functional>
#include <unordered_set>
#include "debug_logger.h"
#include "mem_map_item.h"
#include "perf_event_record.h"
#include "symbols_file.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
/*
03284000-03289000 r--p 00000000 b3:05 289 /system/bin/sh
032b7000-032b9000 rw-p 00000000 00:00 0
aff60000-aff96000 r--p 00000000 b3:05 923 /system/lib/libc++.so
affeb000-affed000 rw-p 00000000 00:00 0
b0023000-b0024000 r--p 00000000 b3:05 959 /system/lib/libdl.so
*/
const std::string MMAP_NAME_HEAP = "[heap]";
const std::string MMAP_NAME_ANON = "[anon]";
class VirtualThread {
public:
VirtualThread(const VirtualThread &) = delete;
VirtualThread &operator=(const VirtualThread &) = delete;
VirtualThread(pid_t pid, const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
: pid_(pid),
tid_(pid),
symbolsFiles_(symbolsFiles),
processMemMaps_(),
memMaps_(processMemMaps_),
parent_(*this) {};
VirtualThread(pid_t pid, pid_t tid, VirtualThread &thread,
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
: pid_(pid),
tid_(tid),
symbolsFiles_(symbolsFiles),
processMemMaps_(),
memMaps_(thread.processMemMaps_),
parent_(thread)
{
HLOG_ASSERT(pid != tid);
HLOGV("%d %d map from parent size is %zu", pid, tid, memMaps_.size());
};
pid_t pid_;
pid_t tid_;
std::string name_;
const std::vector<MemMapItem> &GetMaps() const
{
return memMaps_;
}
void ParseMap();
void CreateMapItem(const std::string filename, uint64_t begin, uint64_t len, uint64_t offset);
const MemMapItem *FindMapByAddr(uint64_t addr) const;
const MemMapItem *FindMapByAddr2(uint64_t addr) const;
const MemMapItem *FindMapByFileInfo(const std::string name, uint64_t offset) const;
SymbolsFile *FindSymbolsFileByMap(const MemMapItem &inMap) const;
bool ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const;
#ifdef HIPERF_DEBUG
void ReportVaddrMapMiss(uint64_t vaddr) const;
#endif
// caller want to check if new mmap is legal
static bool IsLegalFileName(const std::string &filename);
private:
void SortMemMaps();
#ifdef DEBUG_TIME
bool IsSorted() const;
#endif
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles_;
// proc/xx/map
// use to put the parent thread's map
// only process have memmap
std::vector<MemMapItem> processMemMaps_;
// thread must use ref from process
std::vector<MemMapItem> &memMaps_;
VirtualThread &parent_;
#ifdef HIPERF_DEBUG
mutable std::unordered_set<uint64_t> missedRuntimeVaddr_;
#endif
#ifdef DEBUG_MISS_SYMBOL
mutable std::vector<std::string> missedSymbolFile_;
#endif
FRIEND_TEST(VirtualThreadTest, ReadRoMemory);
};
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

View File

@ -0,0 +1,41 @@
# Copyright (C) 2021 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.
import("//build/ohos.gni")
import("//developtools/hiperf/hiperf.gni")
config("hiperf_client_config") {
visibility = [ ":*" ]
include_dirs = [
"include" ,
"${hiperf_path}/include",
]
}
ohos_shared_library("hiperf_client") {
install_enable = true
public_configs = [ ":hiperf_client_config" ]
sources = [ "src/hiperf_client.cpp" ]
defines = []
if(is_linux) {
defines += [ "CONFIG_NO_HILOG" ]
} else {
external_deps = [
"hiviewdfx_hilog_native:libhilog",
]
}
subsystem_name = "developtools"
part_name = "hiperf"
}

View File

@ -0,0 +1,326 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_CLIENT_H_
#define HIPERF_CLIENT_H_
#include <chrono>
#include <string>
#include <vector>
namespace OHOS {
namespace Developtools {
namespace HiPerf {
namespace HiperfClient {
static const std::string HiperfCommandName = "hiperf";
static const std::string SystemBinPath = "/system/bin/";
static const std::string TempBinPath = "/data/local/tmp/";
static const std::string CurrentPath = "./";
static const std::string PerfDataName = "perf.data";
static const std::string ReplyOK = "OK\n";
static const std::string ReplyFAIL = "FAIL\n";
static const std::string ReplyStart = "START\n";
static const std::string ReplyStop = "STOP\n";
static const std::string ReplyPause = "PAUSE\n";
static const std::string ReplyResume = "RESUME\n";
static const std::string ReplyCheck = "CHECK\n";
static const std::string CommandRecord = "record";
static const std::string ArgOutputPath = "-o";
static const std::string ArgDebug = "--verbose";
static const std::string ArgDebugMuch = "--much";
static const std::string ArgHilog = "--hilog";
static const std::string ArgPipeInput = "--pipe_input";
static const std::string ArgPipeOutput = "--pipe_output";
static const std::string ArgTargetSystemWide = "-a";
static const std::string ArgCompressData = "-z";
static const std::string ArgSelectCpus = "-c";
static const std::string ArgTimeStopSec = "-d";
static const std::string ArgFrequency = "-f";
static const std::string ArgPeriod = "--period";
static const std::string ArgSelectEvents = "-e";
static const std::string ArgSelectGroups = "-g";
static const std::string ArgNoInherit = "--no-inherit";
static const std::string ArgSelectPids = "-p";
static const std::string ArgSelectTids = "-t";
static const std::string ArgExcludePerf = "--exclude-hiperf";
static const std::string ArgCpuPercent = "--cpu-limit";
static const std::string ArgOffCPU = "--offcpu";
static const std::string ArgCallGraph = "--call-stack";
static const std::string ArgDelayUnwind = "--delay-unwind";
static const std::string ArgDisableUnwind = "--disable-unwind";
static const std::string ArgDisableCallstackMerge = "--disable-callstack-expend";
static const std::string ArgSymbolDir = "--symbol-dir";
static const std::string ArgOutputFilename = "-o";
static const std::string ArgDataLimit = "--data-limit";
static const std::string ArgAppPackage = "--app";
static const std::string ArgClockId = "--clockid";
static const std::string ArgVecBranchSampleTypes = "-j";
static const std::string ArgMmapPages = "-m";
class RecordOption {
public:
/**
* Set output file name, default is perf.data
*/
void SetOutputFilename(const std::string &outputFilename)
{
outputFileName_ = outputFilename;
}
/**
* Get output file name
*/
const std::string GetOutputFileName() const
{
return outputFileName_;
}
/**
* Collect system-wide information for measures all processes/threads
* default is disable.
*/
void SetTargetSystemWide(bool enable);
/**
* Compress record data. default is disable.
*/
void SetCompressData(bool enable);
/**
* Specify cpu ID, cpu ID shoule be 0,1,2...
*/
void SetSelectCpus(const std::vector<int> &cpus);
/**
* Stop in <timeStopSec> seconds. default is 10000 seconds
*/
void SetTimeStopSec(int timeStopSec);
/**
* Set event sampling frequency. default is 4000 samples every second.
*/
void SetFrequency(int frequency);
/**
* Set event sampling period for tracepoint events.
* recording one sample when <period> events happen.
* default is 1
*/
void SetPeriod(int period);
/**
* Customize the name of the event that needs to be sampled.
*/
void SetSelectEvents(const std::vector<std::string> &selectEvents);
/**
* Customize the name of the event that needs to be grouped.
* the function is added on the basis of the function of the SetSelectEvents().
*/
void SetSelectGroups(const std::vector<std::string> &selectGroups);
/**
* Set to don't tracing child processes. default is disable
*/
void SetNoInherit(bool enable);
/**
* Set the limit process id of the collection target.
* Conflicts with the SetTargetSystemWide(true).
*/
void SetSelectPids(const std::vector<pid_t> &selectPids);
/**
* Set the limit thread id of the collection target.
* Conflicts with the SetTargetSystemWide(true).
*/
void SetSelectTids(const std::vector<pid_t> &selectTids);
/**
* Set dont record events issued by hiperf itself.
*/
void SetExcludePerf(bool excludePerf);
/**
* Set the max percent of cpu time used for recording.
* percent is in range [1-100], default is 25
*/
void SetCpuPercent(int cpuPercent);
/**
* Set tracing when threads are scheduled off cpu, default is disable
*/
void SetOffCPU(bool offCPU);
/**
* Set call-graph (stack chain/backtrace) recording, Default is 'fp'.
* as the method to collect the information used to show the call graphs.
* the value can be:
* fp: frame pointer
* dwarf: DWARF's CFI - Call Frame Information
* 'dwarf,###' set sample stack size, size should be in 8~65528 and 8 byte aligned.
*/
void SetCallGraph(const std::string &sampleTypes);
/**
* Set to unwind after recording.
* If '-g dwarf' used, stack will be unwind while recording by default
*/
void SetDelayUnwind(bool delayUnwind);
/**
* Set to disable unwinding.
* If '-g dwarf' used, stack will be unwind while recording by default
*/
void SetDisableUnwind(bool disableUnwind);
/**
* Set callstack don't merged.
* If '-g dwarf' is used, to break the 64k stack limit, callstack is merged by default
*/
void SetDisableCallstackMerge(bool disableCallstackMerge);
/**
* Set directory to look for symbol files, used for unwinding.
*/
void SetSymbolDir(const std::string &symbolDir_);
/**
* Set to stop recording after <SIZE> bytes of records. Default is unlimited
* format like: SIZE[K|M|G]
*/
void SetDataLimit(const std::string &limit);
/**
* Set a OHOS app name, collect profile info for this app, the app must be debuggable.
*/
void SetAppPackage(const std::string &appPackage);
/**
* Set the clock id to use for the various time fields in the perf_event_type records.
*/
void SetClockId(const std::string &clockId);
/**
* Set to take branch stack sampling, filter can be
* any: any type of branch
* any_call: any function call or system call
* any_ret: any function return or system call return
* ind_call: any indirect branch
* call: direct calls, including far (to/from kernel) calls
* u: only when the branch target is at the user level
* k: only when the branch target is in the kernel\n"
*/
void SetVecBranchSampleTypes(const std::vector<std::string> &vecBranchSampleTypes);
/**
* Set the size of the buffer used to receiving sample data from kernel,
* must be a power of two. If not set, a value <=1024 will be used.
*/
void SetMmapPages(int mmapPages);
/**
* Get the string vector of all options.
*/
const std::vector<std::string> &GetOptionVecString() const
{
return args_;
}
private:
std::vector<std::string> args_ = {};
std::string outputFileName_ = "";
void SetOption(const std::string &name, bool enable);
void SetOption(const std::string &name, int value);
void SetOption(const std::string &name, const std::vector<int> &vInt);
void SetOption(const std::string &name, const std::string &str);
void SetOption(const std::string &name, const std::vector<std::string> &vStr);
};
class Client {
public:
/**
* Set output dir and constuct
*/
Client(const std::string &outputDir = TempBinPath);
~Client();
/**
* Start record with default options
*/
bool Start();
/**
* Start record with options of string vector
*/
bool Start(const std::vector<std::string> &args);
/**
* Start record with options of RecordOption
*/
bool Start(const RecordOption &option);
/**
* Pause recording
*/
bool Pause();
/**
* Resume recording
*/
bool Resume();
/**
* Stop recording
*/
bool Stop();
/**
* Check the client is ready
*/
bool IsReady();
/**
* Set the output dir
*/
bool Setup(std::string outputDir);
/**
* Get the output dir
*/
const std::string &GetOutputDir() const
{
return outputDir_;
}
/**
* Get the command path
*/
const std::string &GetCommandPath() const
{
return executeCommandPath_;
}
/**
* Get the the fullpath of output file
*/
const std::string GetOutputPerfDataPath() const
{
return outputDir_ + outputFileName_;
}
void SetDebugMode();
void SetDebugMuchMode();
void EnableHilog();
private:
static const int PIPE_READ = 0;
static const int PIPE_WRITE = 1;
static constexpr size_t SIZE_ARGV_TAIL = 1; // nullptr
static constexpr int64_t THOUSAND = 1000;
bool WaitCommandReply(std::chrono::milliseconds = std::chrono::milliseconds(THOUSAND));
bool SendCommandAndWait(const std::string &cmd);
void KillChild();
void GetExecCmd(std::vector<std::string> &cmd, int pipeIn, int pipeOut,
const std::vector<std::string> &args);
std::string outputDir_ = "";
std::string outputFileName_ = "";
std::string executeCommandPath_ = "";
bool ready_ = false;
pid_t myPid_ = false;
pid_t myTid_ = false;
bool debug_ = false;
bool debugMuch_ = false;
bool hilog_ = false;
int clientToServerFd_ = -1;
int serverToClientFd_ = -1;
pid_t hperfPid_ = -1;
};
} // namespace HiperfClient
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
#endif

View File

@ -0,0 +1,565 @@
/*
* Copyright (c) 2021 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.
*/
#include "hiperf_client.h"
#include <algorithm>
#include <cerrno>
#include <chrono>
#include <cinttypes>
#include <csignal>
#include <cstring>
#include <thread>
#include <fcntl.h>
#include <poll.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "hiperf_hilog.h"
using namespace std::chrono;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
namespace HiperfClient {
void RecordOption::SetOption(const std::string &name, bool enable)
{
auto it = std::find(args_.begin(), args_.end(), name);
if (enable) {
if (it == args_.end()) {
args_.emplace_back(name);
}
return;
}
if (it != args_.end()) {
args_.erase(it);
return;
}
}
void RecordOption::SetOption(const std::string &name, int value)
{
auto it = std::find(args_.begin(), args_.end(), name);
if (it != args_.end()) {
it++;
*it = std::to_string(value);
return;
}
args_.emplace_back(name);
args_.emplace_back(std::to_string(value));
return;
}
void RecordOption::SetOption(const std::string &name, const std::vector<int> &vInt)
{
auto it = std::find(args_.begin(), args_.end(), name);
if (vInt.empty()) {
if (it != args_.end()) {
args_.erase(it);
args_.erase(it); // remove value
}
return;
}
std::string str;
for (auto n : vInt) {
str.append(std::to_string(n));
str.append(",");
}
str.pop_back(); // remove the last ','
if (it != args_.end()) {
it++;
*it = str;
return;
}
args_.emplace_back(name);
args_.emplace_back(str);
}
void RecordOption::SetOption(const std::string &name, const std::string &str)
{
auto it = std::find(args_.begin(), args_.end(), name);
if (str.empty()) {
if (it != args_.end()) {
args_.erase(it);
args_.erase(it); // remove value
}
return;
}
if (it != args_.end()) {
it++;
*it = str;
return;
}
args_.emplace_back(name);
args_.emplace_back(str);
}
void RecordOption::SetOption(const std::string &name, const std::vector<std::string> &vStr)
{
auto it = std::find(args_.begin(), args_.end(), name);
if (vStr.empty()) {
if (it != args_.end()) {
args_.erase(it);
args_.erase(it); // remove value
}
return;
}
std::string str;
for (auto substr : vStr) {
str.append(substr);
str.append(",");
}
str.pop_back(); // remove the last ','
if (it != args_.end()) {
it++;
*it = str;
return;
}
args_.emplace_back(name);
args_.emplace_back(str);
}
void RecordOption::SetTargetSystemWide(bool enable)
{
SetOption(ArgTargetSystemWide, enable);
}
void RecordOption::SetCompressData(bool enable)
{
SetOption(ArgCompressData, enable);
}
void RecordOption::SetSelectCpus(const std::vector<int> &cpus)
{
SetOption(ArgSelectCpus, cpus);
}
void RecordOption::SetTimeStopSec(int timeStopSec)
{
SetOption(ArgTimeStopSec, timeStopSec);
}
void RecordOption::SetFrequency(int frequency)
{
SetOption(ArgFrequency, frequency);
}
void RecordOption::SetPeriod(int period)
{
SetOption(ArgPeriod, period);
}
void RecordOption::SetSelectEvents(const std::vector<std::string> &selectEvents)
{
SetOption(ArgSelectEvents, selectEvents);
}
void RecordOption::SetSelectGroups(const std::vector<std::string> &selectGroups)
{
SetOption(ArgSelectGroups, selectGroups);
}
void RecordOption::SetNoInherit(bool enable)
{
SetOption(ArgNoInherit, enable);
}
void RecordOption::SetSelectPids(const std::vector<pid_t> &selectPids)
{
SetOption(ArgSelectPids, selectPids);
}
void RecordOption::SetSelectTids(const std::vector<pid_t> &selectTids)
{
SetOption(ArgSelectTids, selectTids);
}
void RecordOption::SetExcludePerf(bool excludePerf)
{
SetOption(ArgExcludePerf, excludePerf);
}
void RecordOption::SetCpuPercent(int cpuPercent)
{
SetOption(ArgCpuPercent, cpuPercent);
}
void RecordOption::SetOffCPU(bool offCPU)
{
SetOption(ArgOffCPU, offCPU);
}
void RecordOption::SetCallGraph(const std::string &callGraph)
{
SetOption(ArgCallGraph, callGraph);
}
void RecordOption::SetDelayUnwind(bool delayUnwind)
{
SetOption(ArgDelayUnwind, delayUnwind);
}
void RecordOption::SetDisableUnwind(bool disableUnwind)
{
SetOption(ArgDisableUnwind, disableUnwind);
}
void RecordOption::SetDisableCallstackMerge(bool disableCallstackMerge)
{
SetOption(ArgDisableCallstackMerge, disableCallstackMerge);
}
void RecordOption::SetSymbolDir(const std::string &symbolDir_)
{
SetOption(ArgSymbolDir, symbolDir_);
}
void RecordOption::SetDataLimit(const std::string &limit)
{
SetOption(ArgDataLimit, limit);
}
void RecordOption::SetAppPackage(const std::string &appPackage)
{
SetOption(ArgAppPackage, appPackage);
}
void RecordOption::SetClockId(const std::string &clockId)
{
SetOption(ArgClockId, clockId);
}
void RecordOption::SetVecBranchSampleTypes(const std::vector<std::string> &vecBranchSampleTypes)
{
SetOption(ArgVecBranchSampleTypes, vecBranchSampleTypes);
}
void RecordOption::SetMmapPages(int mmapPages)
{
SetOption(ArgMmapPages, mmapPages);
}
Client::Client(const std::string &outputDir)
{
HIPERF_HILOGD(MODULE_CPP_API, "%" HILOG_PUBLIC "s default init with %" HILOG_PUBLIC "s\n",
__FUNCTION__, outputDir.c_str());
Setup(outputDir);
// review: maybe change to some nice check , not just ign th pipe broken?
signal(SIGPIPE, SIG_IGN);
}
bool Client::Setup(std::string outputDir)
{
std::string CurrentCommandPath = CurrentPath + HiperfCommandName;
std::string SystemCommandPath = SystemBinPath + HiperfCommandName;
std::string TempCommandPath = TempBinPath + HiperfCommandName;
if (!outputDir.empty() && outputDir.back() != '/') {
outputDir.push_back('/');
}
HIPERF_HILOGD(MODULE_CPP_API, "outputDir setup to %" HILOG_PUBLIC "s\n", outputDir.c_str());
// found command path
if (access(CurrentCommandPath.c_str(), X_OK) == 0) {
executeCommandPath_ = CurrentCommandPath;
} else if (access(TempCommandPath.c_str(), X_OK) == 0) {
executeCommandPath_ = TempCommandPath;
} else if (access(SystemCommandPath.c_str(), X_OK) == 0) {
executeCommandPath_ = SystemCommandPath;
} else {
HIPERF_HILOGD(MODULE_CPP_API, "no hiperf command found\n");
return ready_;
}
// check output path
// found command path
if (access(outputDir.c_str(), W_OK) == 0) {
outputDir_ = outputDir;
} else if (access(CurrentPath.c_str(), W_OK) == 0) {
outputDir_ = CurrentPath;
} else {
HIPERF_HILOGD(MODULE_CPP_API, "no writeable output path found\n");
return ready_;
}
outputFileName_ = PerfDataName;
myPid_ = getpid();
#ifndef gettid
myTid_ = syscall(SYS_gettid);
#else
myTid_ = gettid();
#endif
// every thing check ok
ready_ = true;
return ready_;
}
Client::~Client()
{
KillChild();
}
bool Client::IsReady()
{
return ready_;
}
void Client::SetDebugMode()
{
debug_ = true;
}
void Client::SetDebugMuchMode()
{
debugMuch_ = true;
}
bool Client::Start()
{
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
std::vector<std::string> args;
args.push_back("-p");
args.push_back(std::to_string(getpid()));
return Start(args);
}
void Client::GetExecCmd(std::vector<std::string> &cmd, int pipeIn, int pipeOut,
const std::vector<std::string> &args)
{
cmd.clear();
cmd.emplace_back(executeCommandPath_);
if (debug_) {
cmd.emplace_back(ArgDebug);
} else if (debugMuch_) {
cmd.emplace_back(ArgDebugMuch);
}
if (hilog_) {
cmd.emplace_back(ArgHilog);
}
cmd.emplace_back(CommandRecord);
cmd.emplace_back(ArgPipeInput);
cmd.emplace_back(std::to_string(pipeIn));
cmd.emplace_back(ArgPipeOutput);
cmd.emplace_back(std::to_string(pipeOut));
cmd.emplace_back(ArgOutputPath);
cmd.emplace_back(GetOutputPerfDataPath());
cmd.insert(cmd.end(), args.begin(), args.end());
}
bool Client::Start(const std::vector<std::string> &args)
{
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
if (!ready_) {
HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
return false;
}
int clientToServerFd[2];
int serverToClientFd[2];
if (pipe(clientToServerFd) != 0 || pipe(serverToClientFd) != 0) {
HIPERF_HILOGD(MODULE_CPP_API, "failed to create pipe: %" HILOG_PUBLIC "s", strerror(errno));
return false;
}
hperfPid_ = fork();
if (hperfPid_ == -1) {
HIPERF_HILOGD(MODULE_CPP_API, "failed to fork: %" HILOG_PUBLIC "s", strerror(errno));
return false;
} else if (hperfPid_ == 0) {
// child process
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
close(clientToServerFd[PIPE_WRITE]);
close(serverToClientFd[PIPE_READ]);
std::vector<std::string> cmd;
GetExecCmd(cmd, clientToServerFd[PIPE_READ], serverToClientFd[PIPE_WRITE], args);
// conver vector to array for execvp()
char *argv[cmd.size() + SIZE_ARGV_TAIL];
size_t i = 0;
for (i = 0; i < cmd.size(); ++i) {
HIPERF_HILOGD(MODULE_CPP_API, "args %" HILOG_PUBLIC "zu : %" HILOG_PUBLIC "s", i,
cmd[i].c_str());
argv[i] = cmd[i].data();
}
argv[i] = nullptr;
execv(argv[0], argv);
HIPERF_HILOGD(MODULE_CPP_API,
"failed to call exec: '%" HILOG_PUBLIC "s' %" HILOG_PUBLIC "s\n",
executeCommandPath_.c_str(), strerror(errno));
exit(0);
} else {
// parent process
close(clientToServerFd[PIPE_READ]);
close(serverToClientFd[PIPE_WRITE]);
clientToServerFd_ = clientToServerFd[PIPE_WRITE];
serverToClientFd_ = serverToClientFd[PIPE_READ];
}
using namespace std::chrono_literals;
if (!WaitCommandReply(1000ms)) {
HIPERF_HILOGD(MODULE_CPP_API, "start failed . lets kill it");
KillChild();
return false;
}
return true;
}
bool Client::Start(const RecordOption &option)
{
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
if (!option.GetOutputFileName().empty()) {
outputFileName_ = option.GetOutputFileName();
}
return Start(option.GetOptionVecString());
}
bool Client::WaitCommandReply(std::chrono::milliseconds timeOut)
{
std::string reply;
struct pollfd pollFd;
pollFd.fd = serverToClientFd_;
pollFd.events = POLLIN;
pollFd.revents = 0;
// wait some data
int polled = poll(&pollFd, 1, timeOut.count());
if (polled > 0) {
while (true) {
char c;
size_t result = TEMP_FAILURE_RETRY(read(serverToClientFd_, &c, 1));
if (result <= 0) {
HIPERF_HILOGD(MODULE_CPP_API, "read failed from pipe");
return false; // read fial means not ok
}
reply.push_back(c);
if (c == '\n') {
break;
}
}
} else if (polled == 0) {
HIPERF_HILOGD(MODULE_CPP_API, "Client:command no response %" HILOG_PUBLIC "" PRIu64 ".\n",
timeOut.count());
} else {
HIPERF_HILOGD(MODULE_CPP_API, "Client:command poll failed.\n");
}
HIPERF_HILOGD(MODULE_CPP_API, "Client:new reply:%" HILOG_PUBLIC "s\n", reply.c_str());
if (reply == ReplyOK) {
return true;
} else {
return false;
}
}
void Client::KillChild()
{
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
if (clientToServerFd_ != -1) {
close(clientToServerFd_);
}
if (serverToClientFd_ != -1) {
close(serverToClientFd_);
}
if (hperfPid_ > 0) {
kill(hperfPid_, SIGINT);
}
}
bool Client::SendCommandAndWait(const std::string &cmd)
{
if (clientToServerFd_ == -1) {
HIPERF_HILOGD(MODULE_CPP_API, "fd not ready. maybe not called start.");
return false;
}
size_t size = write(clientToServerFd_, cmd.c_str(), cmd.size());
HIPERF_HILOGD(MODULE_CPP_API,
"Client:%" HILOG_PUBLIC "s -> %" HILOG_PUBLIC "d : %" HILOG_PUBLIC "zd\n",
cmd.c_str(), clientToServerFd_, size);
if (size == cmd.size()) {
return WaitCommandReply();
} else {
return false;
}
}
bool Client::Pause()
{
if (!ready_) {
HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
return false;
}
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
if (SendCommandAndWait(ReplyPause)) {
return true;
}
return false;
}
bool Client::Resume()
{
if (!ready_) {
HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
return false;
}
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
if (SendCommandAndWait(ReplyResume)) {
return true;
}
return false;
}
bool Client::Stop()
{
if (!ready_) {
HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
return false;
}
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
if (SendCommandAndWait(ReplyStop)) {
// wait sampling process exit really
while (SendCommandAndWait(ReplyCheck)) {
std::this_thread::sleep_for(1s);
}
return true;
}
return false;
}
void Client::EnableHilog()
{
HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
hilog_ = true;
}
} // namespace HiperfClient
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS

249
interfaces/kits/js/@ohos.hiperf.d.ts vendored Normal file
View File

@ -0,0 +1,249 @@
/*
* Copyright (C) 2021 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.
*/
/**
* Provides interfaces to trace a task for performance measure by hiperf.
* result can be use by hiperf python script
*
* @devices phone, tablet
* @since 7
*/
declare namespace hiperf {
/**
* Start record when RecordOption pass to Client
*/
function startWithOption(): boolean;
/**
* Reset the option
*/
function resetOption(): boolean
/**
* Start record with default options
*/
function start(): boolean;
/**
* Pause recording
*/
function pause(): boolean;
/**
* Resume recording
*/
function resume(): boolean;
/**
* Stop recording
*/
function stop(): boolean;
/**
* Check the client is ready
*/
function isReady(): boolean;
/**
* Set the output dir
* @param directory
*/
function setup(outputDir: string): boolean;
/**
* Get the output dir
*/
function getOutputDir(): string;
/**
* Get the command path
*/
function getCommandPath(): string;
/**
* Get the the fullpath of output file
*/
function getOutputPerfDataPath(): string;
function setDebugMode(): boolean;
/**
* Set output file name, default is perf.data
* @param Filename
*/
function setOutputFilename(outputFilename: string): boolean;
/**
* Get output file name
*/
function getOutputFileName(): string;
/**
* Collect system-wide information for measures all processes/threads
* @param default is disable.
*/
function setTargetSystemWide(enble: boolean): boolean;
/**
* Compress record data.
* @param default is disable.
*/
function setCompressData(enble: boolean): boolean;
/**
* Specify cpu ID
* @param cpu ID should 0,1,2...
*/
function setSelectCpus(cpus: number[]): boolean;
/**
* Stop in <timeStopSec> seconds.
* @param default is 10000 seconds
*/
function setTimeStopSec(timeStopSec: number): boolean;
/**
* Set event sampling frequency.
* @param default is 4000 samples every second.
*/
function setFrequency(frequency: number): boolean;
/**
* Set event sampling period for tracepoint events.
* recording one sample when <period> events happen.
* @param default is 1
*/
function setPeriod(period: number): boolean;
/**
* Customize the name of the event that needs to be sampled.
*/
function setSelectEvents(selectEvents: string[]): boolean;
/**
* Customize the name of the event that needs to be grouped.
* the function is added on the basis of the function of the SetSelectEvents().
*/
function setSelectGroups(selectGroups: string[]): boolean;
/**
* Set to don't tracing child processes.
* param default is disable
*/
function setNoInherit(enable: boolean): boolean;
/**
* Set the limit process id of the collection target.
* Conflicts with the SetTargetSystemWide(true).
*/
function setSelectPids(selectPids: number[]): boolean;
/**
* Set the limit thread id of the collection target.
* Conflicts with the SetTargetSystemWide(true).
*/
function setSelectTids(selectTids: number[]): boolean;
/**
* Set dont record events issued by hiperf itself.
*/
function setExcludePerf(excludePerf: boolean): boolean;
/**
* Set the max percent of cpu time used for recording.
* percent is in range [1-100]
* @param default is 25
*/
function setCpuPercent(cpuPercent: number): boolean;
/**
* Set tracing when threads are scheduled off cpu
* @param default is disable
*/
function setOffCPU(offCPU: boolean): boolean;
/**
* Set call-graph (stack chain/backtrace) recording, Default is 'fp'.
* as the method to collect the information used to show the call graphs.
* the value can be:
* fp: frame pointer
* dwarf: DWARF's CFI - Call Frame Information
* 'dwarf,###' set sample stack size, size should be in 8~65528 and 8 byte aligned.
*/
function setCallGraph(sampleTypes: string): boolean;
/**
* Set to unwind after recording.
* If '-g dwarf' used, stack will be unwind while recording by default
*/
function setDelayUnwind(delayUnwind: boolean): boolean;
/**
* Set to disable unwinding.
* If '-g dwarf' used, stack will be unwind while recording by default
*/
function setDisableUnwind(disableUnwind: boolean): boolean;
/**
* Set callstack don't merged.
* If '-g dwarf' is used, to break the 64k stack limit, callstack is merged by default
*/
function setDisableCallstackMerge(disableCallstackMerge: boolean): boolean;
/**
* Set directory to look for symbol files, used for unwinding.
*/
function setSymbolDir(symbolDir_: string): boolean;
/**
* Set to stop recording after <SIZE> bytes of records. Default is unlimited
* format like: SIZE[K|M|G]
*/
function setDataLimit(limit: string): boolean;
/**
* Set a OHOS app name, collect profile info for this app, the app must be debuggable.
*/
function setAppPackage(appPackage: string): boolean;
/**
* Set the clock id to use for the various time fields in the perf_event_type records.
*/
function setClockId(clockId: string): boolean;
/**
* Set to take branch stack sampling, filter can be
* any: any type of branch
* any_call: any function call or system call
* any_ret: any function return or system call return
* ind_call: any indirect branch
* call: direct calls, including far (to/from kernel) calls
* u: only when the branch target is at the user level
* k: only when the branch target is in the kernel\n"
*/
function setVecBranchSampleTypes(vecBranchSampleTypes: string[]): boolean;
/**
* Set the size of the buffer used to receiving sample data from kernel,
* must be a power of two. If not set, a value <=1024 will be used.
*/
function setMmapPages(mmapPages: number): boolean;
/**
* Get the string vector of all options.
*/
function getOptionVecString(): string[];
}
export default hiperf;

View File

@ -0,0 +1,44 @@
# Copyright (c) 2021 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.
import("//build/ohos.gni")
import("//developtools/hiperf/hiperf.gni")
config("hiperf_client_napi_config") {
include_dirs = [
"//foundation/ace/napi/interfaces/kits",
"//third_party/node/src",
"${hiperf_path}/include",
"${innerkits_path}/include",
]
}
ohos_shared_library("hiperf_client_napi") {
install_enable = true
configs = [ ":hiperf_client_napi_config" ]
sources = [ "hiperf_client_napi.cpp" ]
deps = [
"//foundation/ace/napi:ace_napi",
"${innerkits_path}/native:hiperf_client",
]
external_deps = [
"hiviewdfx_hilog_native:libhilog",
]
output_name = "hiperf_napi"
relative_install_dir = "module"
subsystem_name = "developtools"
part_name = "hiperf"
}

View File

@ -0,0 +1,688 @@
/*
* Copyright (c) 2021 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.
*/
#include "hiperf_client_napi.h"
#include <cstdio>
#include <cstdlib>
#include <string>
#include "hiperf_hilog.h"
#include "hiperf_client.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
namespace HiperfClient {
static std::unique_ptr<HiperfClient::Client> g_hiperfClient =
std::make_unique<HiperfClient::Client>();
static std::unique_ptr<HiperfClient::RecordOption> g_hiperfRecordOption =
std::make_unique<HiperfClient::RecordOption>();
static std::vector<std::string> StringSplit(std::string source, std::string split = ",")
{
size_t pos = 0;
std::vector<std::string> result;
// find
while ((pos = source.find(split)) != std::string::npos) {
// split
std::string token = source.substr(0, pos);
if (!token.empty()) {
result.push_back(token);
}
source.erase(0, pos + split.length());
}
// add last token
if (!source.empty()) {
result.push_back(source);
}
return result;
}
static std::vector<int> StringSplitToInt(std::string source, std::string split = ",")
{
size_t pos = 0;
std::vector<int> result;
// find
while ((pos = source.find(split)) != std::string::npos) {
// split
std::string token = source.substr(0, pos);
if (!token.empty()) {
result.push_back(std::stoi(token));
}
source.erase(0, pos + split.length());
}
// add last token
if (!source.empty()) {
result.push_back(std::stoi(source));
}
return result;
}
static std::string GetJsStringFromOption(const napi_env &env, const napi_callback_info &info)
{
size_t argc = 1;
napi_value args[1] = {0};
NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "");
NAPI_ASSERT_BASE(env, argc == 1, "requires 1 parameter", "");
napi_valuetype inputType = napi_undefined;
napi_typeof(env, args[0], &inputType);
NAPI_ASSERT_BASE(env, inputType == napi_string, "type mismatch for parameter path", "");
char value[PATH_MAX] = {0};
size_t valueLen = 0;
napi_get_value_string_utf8(env, args[0], value, sizeof(value), &valueLen);
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}s", value);
return std::string(value);
}
static bool GetBoolFromOption(const napi_env &env, const napi_callback_info &info)
{
size_t argc = 1;
napi_value args[1] = {0};
NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), false);
NAPI_ASSERT_BASE(env, argc == 1, "requires 1 parameter", false);
napi_valuetype inputType = napi_undefined;
napi_typeof(env, args[0], &inputType);
NAPI_ASSERT_BASE(env, (inputType == napi_boolean), "type mismatch for parameter path", false);
bool result = false;
napi_get_value_bool(env, args[0], &result);
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return result;
}
static uint32_t GetUintFromOption(const napi_env &env, const napi_callback_info &info)
{
size_t argc = 1;
napi_value args[1] = {0};
NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), 0);
NAPI_ASSERT_BASE(env, argc == 1, "requires 1 parameter", 0);
napi_valuetype inputType = napi_undefined;
napi_typeof(env, args[0], &inputType);
NAPI_ASSERT_BASE(env, (inputType == napi_number), "type mismatch for parameter path", false);
uint32_t result = 0;
napi_get_value_uint32(env, args[0], &result);
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return result;
}
static napi_value ResetOption(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
g_hiperfRecordOption = std::make_unique<HiperfClient::RecordOption>();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetOutputFilename(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
const std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetOutputFilename(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value GetOutputFileName(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
std::string result = g_hiperfRecordOption->GetOutputFileName();
NAPI_CALL(env, napi_create_string_utf8(env, result.c_str(), result.size(), &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}s", result.c_str());
return napiValue;
}
static napi_value SetTargetSystemWide(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetTargetSystemWide(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetCompressData(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetCompressData(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetSelectCpus(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetSelectCpus(StringSplitToInt(option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetTimeStopSec(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
uint32_t option = GetUintFromOption(env, info);
g_hiperfRecordOption->SetTimeStopSec(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetFrequency(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
uint32_t option = GetUintFromOption(env, info);
g_hiperfRecordOption->SetFrequency(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetPeriod(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
uint32_t option = GetUintFromOption(env, info);
g_hiperfRecordOption->SetPeriod(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetSelectEvents(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetSelectEvents(StringSplit(option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetSelectGroups(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetSelectGroups(StringSplit(option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetNoInherit(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetNoInherit(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetSelectPids(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetSelectPids(StringSplitToInt(option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
};
static napi_value SetSelectTids(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetSelectTids(StringSplitToInt(option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetExcludePerf(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetExcludePerf(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetCpuPercent(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
uint32_t option = GetUintFromOption(env, info);
g_hiperfRecordOption->SetCpuPercent(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetOffCPU(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetOffCPU(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetCallGraph(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetCallGraph((option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetDelayUnwind(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetDelayUnwind(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetDisableUnwind(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetDisableUnwind(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetDisableCallstackMerge(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
bool enable = GetBoolFromOption(env, info);
g_hiperfRecordOption->SetDisableCallstackMerge(enable);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetSymbolDir(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetSymbolDir(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetDataLimit(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetDataLimit(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetAppPackage(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetAppPackage(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetClockId(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetClockId(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetVecBranchSampleTypes(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
std::string option = GetJsStringFromOption(env, info);
g_hiperfRecordOption->SetVecBranchSampleTypes(StringSplit(option));
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value SetMmapPages(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
uint32_t option = GetUintFromOption(env, info);
g_hiperfRecordOption->SetMmapPages(option);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value GetOptionVecString(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
const std::vector<std::string> items = g_hiperfRecordOption->GetOptionVecString();
std::string result;
const std::string split = ",";
for (auto item : items) {
if (!result.empty())
result.append(split);
result.append(item);
}
if (result.empty()) {
result.append("<empty>");
}
NAPI_CALL(env, napi_create_string_utf8(env, result.c_str(), result.size(), &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}s", result.c_str());
return napiValue;
}
static napi_value StartWithOption(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = false;
// for js api , we always use hilog
g_hiperfClient->EnableHilog();
result = g_hiperfClient->Setup(g_hiperfRecordOption->GetOutputFileName());
if (result) {
const HiperfClient::RecordOption *option = g_hiperfRecordOption.get();
result = g_hiperfClient->Start(*option);
}
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value Start(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
// for js api , we always use hilog
g_hiperfClient->EnableHilog();
bool result = g_hiperfClient->Start();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value Setup(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = false;
std::string outputPath = GetJsStringFromOption(env, info);
// for js api , we always use hilog
g_hiperfClient->EnableHilog();
result = g_hiperfClient->Setup(outputPath);
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value IsReady(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = g_hiperfClient->IsReady();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value Stop(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = g_hiperfClient->Stop();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value Pause(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = g_hiperfClient->Pause();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value Resume(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = g_hiperfClient->Resume();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
static napi_value GetOutputDir(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
std::string result = g_hiperfClient->GetOutputDir();
NAPI_CALL(env, napi_create_string_utf8(env, result.c_str(), result.size(), &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}s", result.c_str());
return napiValue;
}
static napi_value GetCommandPath(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
std::string result = g_hiperfClient->GetCommandPath();
NAPI_CALL(env, napi_create_string_utf8(env, result.c_str(), result.size(), &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}s", result.c_str());
return napiValue;
}
static napi_value GetOutputPerfDataPath(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
std::string result = g_hiperfClient->GetOutputPerfDataPath();
NAPI_CALL(env, napi_create_string_utf8(env, result.c_str(), result.size(), &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}s", result.c_str());
return napiValue;
}
static napi_value SetDebugMode(napi_env env, napi_callback_info info)
{
napi_value napiValue = nullptr;
bool result = true;
g_hiperfClient->SetDebugMode();
NAPI_CALL(env, napi_create_int32(env, result, &napiValue));
HIPERF_HILOGD(MODULE_JS_NAPI, "%{public}d", result);
return napiValue;
}
} // namespace HiperfClient
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS
using namespace OHOS::Developtools::HiPerf::HiperfClient;
EXTERN_C_START
/*
* function for module exports
*/
static napi_value HiperfClientInit(napi_env env, napi_value exports)
{
HIPERF_HILOGD(MODULE_JS_NAPI, "enter");
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("isReady", IsReady),
DECLARE_NAPI_FUNCTION("setup", Setup),
DECLARE_NAPI_FUNCTION("start", Start),
DECLARE_NAPI_FUNCTION("stop", Stop),
DECLARE_NAPI_FUNCTION("pause", Pause),
DECLARE_NAPI_FUNCTION("resume", Resume),
DECLARE_NAPI_FUNCTION("getOutputDir", GetOutputDir),
DECLARE_NAPI_FUNCTION("getOutputPerfDataPath", GetOutputPerfDataPath),
DECLARE_NAPI_FUNCTION("getCommandPath", GetCommandPath),
DECLARE_NAPI_FUNCTION("setDebugMode", SetDebugMode),
// Option:
DECLARE_NAPI_FUNCTION("startWithOption", StartWithOption),
DECLARE_NAPI_FUNCTION("resetOption", ResetOption),
DECLARE_NAPI_FUNCTION("setOutputFilename", SetOutputFilename),
DECLARE_NAPI_FUNCTION("getOutputFileName", GetOutputFileName),
DECLARE_NAPI_FUNCTION("setTargetSystemWide", SetTargetSystemWide),
DECLARE_NAPI_FUNCTION("setCompressData", SetCompressData),
DECLARE_NAPI_FUNCTION("setSelectCpus", SetSelectCpus),
DECLARE_NAPI_FUNCTION("setTimeStopSec", SetTimeStopSec),
DECLARE_NAPI_FUNCTION("setFrequency", SetFrequency),
DECLARE_NAPI_FUNCTION("setPeriod", SetPeriod),
DECLARE_NAPI_FUNCTION("setSelectEvents", SetSelectEvents),
DECLARE_NAPI_FUNCTION("setSelectGroups", SetSelectGroups),
DECLARE_NAPI_FUNCTION("setNoInherit", SetNoInherit),
DECLARE_NAPI_FUNCTION("setSelectPids", SetSelectPids),
DECLARE_NAPI_FUNCTION("setSelectTids", SetSelectTids),
DECLARE_NAPI_FUNCTION("setExcludePerf", SetExcludePerf),
DECLARE_NAPI_FUNCTION("setCpuPercent", SetCpuPercent),
DECLARE_NAPI_FUNCTION("setOffCPU", SetOffCPU),
DECLARE_NAPI_FUNCTION("setCallGraph", SetCallGraph),
DECLARE_NAPI_FUNCTION("setDelayUnwind", SetDelayUnwind),
DECLARE_NAPI_FUNCTION("setDisableUnwind", SetDisableUnwind),
DECLARE_NAPI_FUNCTION("setDisableCallstackMerge", SetDisableCallstackMerge),
DECLARE_NAPI_FUNCTION("setSymbolDir", SetSymbolDir),
DECLARE_NAPI_FUNCTION("setDataLimit", SetDataLimit),
DECLARE_NAPI_FUNCTION("setAppPackage", SetAppPackage),
DECLARE_NAPI_FUNCTION("setClockId", SetClockId),
DECLARE_NAPI_FUNCTION("setVecBranchSampleTypes", SetVecBranchSampleTypes),
DECLARE_NAPI_FUNCTION("setMmapPages", SetMmapPages),
DECLARE_NAPI_FUNCTION("getOptionVecString", GetOptionVecString),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
HIPERF_HILOGD(MODULE_JS_NAPI, "exit");
return exports;
}
EXTERN_C_END
/*
* Module definition
*/
static napi_module g_module = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = HiperfClientInit,
.nm_modname = "hiperf",
.nm_priv = ((void *)0),
.reserved = {0},
};
/*
* Module registration
*/
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(&g_module);
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef HIPERF_CLIENT_NAPI_H
#define HIPERF_CLIENT_NAPI_H
#endif

27
ohos.build Normal file
View File

@ -0,0 +1,27 @@
{
"subsystem": "developtools",
"parts": {
"hiperf": {
"module_list": [
"//developtools/hiperf/:hiperf_target",
"//developtools/hiperf/:hiperf_target_all"
],
"inner_kits": [
{
"type": "so",
"name": "//developtools/hiperf/interfaces/innerkits/native:hiperf_client",
"header": {
"header_files": [
"hiperf_client.h"
],
"header_base": "//developtools/hiperf/interfaces/innerkits/native/include"
}
}
],
"test_list": [
"//developtools/hiperf/test:hiperf_unittest",
"//developtools/hiperf/test:hiperf_fuzztest"
]
}
}
}

36
proto/build_proto.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/bash
# Copyright (c) 2021 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.
set -e
echo "use protoc to do the prebuild (gn unable set LD_LIBRARY_PATH)"
echo "pwd $(pwd)"
MYDIR=$(dirname $0)
BUILD_TOP=$MYDIR/../../../
echo MYDIR $MYDIR
echo BUILD_TOP $BUILD_TOP
protoc_cmdline=$*
echo protoc_cmdline $protoc_cmdline
# search one by bone
PREBUILD_HOST_AOSP_RUNTIME=$BUILD_TOP/prebuilts/aosp_prebuilt_libs/host_tools/lib64
PREBUILD_HOST_CLANG_RUNTIME=$BUILD_TOP/prebuilts/clang/host/linux-x86/clang-r353983c/lib64
PREBUILD_OHOS_CLANG_RUNTIME=$BUILD_TOP/prebuilts/clang/ohos/linux-x86_64/llvm/lib
export LD_LIBRARY_PATH=$PREBUILD_HOST_AOSP_RUNTIME:$PREBUILD_HOST_CLANG_RUNTIME:$PREBUILD_OHOS_CLANG_RUNTIME:$LD_LIBRARY_PATH
cmd="$protoc_cmdline"
echo $cmd
$cmd

91
proto/report_sample.proto Normal file
View File

@ -0,0 +1,91 @@
// Copyright (c) 2021 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.
// report_sample.proto format:
// char magic[10] = "HIPERF_PB_";
// LittleEndian16(version) = 1;
// LittleEndian32(sample_size)
// message Record(sample)
// LittleEndian32(sample_size)
// message Record(sample)
// ...
// LittleEndian32(sample_size)
// message Record(sample)
// LittleEndian32(0)
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package OHOS.Developtools.Hiperf.Proto;
message CallStackSample {
optional uint64 time = 1;
optional uint32 tid = 2;
message CallStackFrame {
// virtual address of the instruction in symbols file
optional uint64 symbols_vaddr = 1;
// index of index of SymbolTableFile::id, base from 0
optional uint32 symbols_file_id = 2;
// index of SymbolTableFile::symbol_name, base from 0
// -1 means not found
optional int32 function_name_id = 3;
}
repeated CallStackFrame callStackFrame = 3;
// not include lost
optional uint64 event_count = 4;
// index of ReportInfo::config_name
optional uint32 config_name_id = 5;
}
message SampleStatistic {
optional uint64 count = 1;
optional uint64 lost = 2;
}
message SymbolTableFile {
// unique id , start from 0
optional uint32 id = 1;
// symbols file path, like developtools/hiperf/hiperf
optional string path = 2;
// function symbol table of the file (always mangled).
repeated string function_name = 3;
}
message VirtualThreadInfo {
optional uint32 tid = 1;
optional uint32 pid = 2;
optional string name = 3;
}
message ReportInfo {
repeated string config_name = 1;
optional string workload_cmd = 2;
}
message HiperfRecord {
oneof RecordType {
CallStackSample sample = 1;
SampleStatistic statistic = 2;
SymbolTableFile file = 3;
VirtualThreadInfo thread = 4;
ReportInfo info= 5;
}
}

114
script/command_script.py Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import os
import time
import argparse
import subprocess
from hiperf_utils import HdcInterface
from hiperf_utils import get_build_id
from hiperf_utils import get_architecture
from hiperf_utils import PerformanceProfile
from hiperf_utils import dir_check
from hiperf_utils import get_hiperf_binary_path
from hiperf_utils import bytes_to_str
from hiperf_utils import str_to_bytes
from hiperf_utils import remove
def check_args(args):
if (not args.package_name) and args.ability:
raise Exception('-a can only be used when profiling an OHOS '
'package_name.')
return True
def parser_add_argument():
description = "Collect performance sampling information of" \
" running [command]."
parser = argparse.ArgumentParser(description=description)
target_group = parser.add_argument_group(title='Select profiling target') \
.add_mutually_exclusive_group(required=True)
target_group.add_argument('-app', '--package_name',
help="""Collect profile info for an OHOS app""")
target_group.add_argument('-lp', '--local_program',
help="""Collect profile info
for an local program.""")
target_group.add_argument('-cmd',
help="""Running a command on the OHOS device.
like as : -cmd "'ps -ef'".
the ps will open as child process of hiperf
and sample this process.""")
target_group.add_argument('-p', '--pid', nargs='*',
help="""Limit the process id of the collection
target.""")
target_group.add_argument('-t', '--tid', nargs='*',
help="""Limit the thread id of the collection
target.""")
target_group.add_argument('-sw', '--system_wide', action='store_true',
help="""Collect system-wide information.
This requires CAP_PERFMON (since Linux 5.8) or
CAP_SYS_ADMIN capability or a
/proc/sys/kernel/perf_event_paranoid
value of less than 1.""")
record_group = parser.add_argument_group('Select recording options')
record_group.add_argument('-a', '--ability',
help="""Used with -p. Profile the launch time of
an ability in an OHOS app. The app will be started or
restarted to run the ability.
Like : -a .MainAbility """)
record_group.add_argument('-r', '--record_options',
default='-f 1000 -d 10 -s dwarf',
help="""Set recording options for `hiperf record`
command. Default is "'-f 1000 -d 10 -s dwarf'".""")
record_group.add_argument('-lib', '--local_lib_dir', type=dir_check,
help="""When profiling an OHOS app containing
local thelocal libraries are usually stripped and lake
of symbols and debug information to provide good
profiling result. By using -lib, you tell
command_script.py the path storing unstripped local
libraries, and script will search all shared libraries
with suffix .so in the directory. Then the local
libraries will be downloaded on device and collected
in build_cache.""")
record_group.add_argument('-o', '--output_perf_data', default='perf.data',
help='The path to store profiling data. '
'Default is perf.data.')
other_group = parser.add_argument_group('Other options')
other_group.add_argument('--not_hdc_root', action='store_true',
help="""Force hdc to run in non root mode. """)
args = parser.parse_args()
return args
def main(args):
check_args(args)
profiler = PerformanceProfile(args)
profiler.profile()
return True
if __name__ == '__main__':
main(parser_add_argument())

553
script/hiperf_utils.py Normal file
View File

@ -0,0 +1,553 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import sys
import subprocess
import argparse
import time
import os
import shutil
import platform
from ctypes import c_char_p
from ctypes import cdll
IS_DEBUG = False
HDC_NAME = "hdc_std"
SYMBOL_FILES_DIR = '/data/local/tmp/local_libs/'
BUILD_ID_FILE = "build_id_list"
EXPECTED_TOOLS = {
HDC_NAME: {
'is_binutils': False,
'test_option': 'version',
'path_in_tool': '../platform-tools/hdc_std',
}
}
def get_lib():
script_dir = os.path.dirname(os.path.realpath(__file__))
system_type = platform.system().lower()
if system_type == "windows":
lib_path = os.path.join(script_dir, "bin", system_type,
"x86_64", "libhiperf_report.dll")
elif system_type == "linux":
lib_path = os.path.join(script_dir, "bin", system_type,
"x86_64", "libhiperf_report.so")
elif system_type == "darwin":
lib_path = os.path.join(script_dir, "bin", system_type,
"x86_64", "libhiperf_report.dylib")
else:
print("Un Support System Platform")
raise RuntimeError
if not os.path.exists(lib_path):
print("{} does not exist!".format(lib_path))
raise RuntimeError
return cdll.LoadLibrary(lib_path)
def remove(files):
if os.path.isfile(files):
os.remove(files)
elif os.path.isdir(files):
shutil.rmtree(files, ignore_errors=True)
def is_elf_file(path):
if os.path.isfile(path):
with open(path, 'rb') as file_bin:
data = file_bin.read(4)
if len(data) == 4 and bytes_to_str(data) == '\x7fELF':
return True
return False
def get_architecture(elf_path):
if is_elf_file(elf_path):
my_lib = get_lib()
my_lib.ReportGetElfArch.restype = c_char_p
ret = my_lib.ReportGetElfArch(elf_path.encode())
return ret.decode()
return 'unknown'
def get_build_id(elf_path):
if is_elf_file(elf_path):
my_lib = get_lib()
my_lib.ReportGetBuildId.restype = c_char_p
ret = my_lib.ReportGetBuildId(elf_path.encode())
return ret.decode()
return ''
def get_hiperf_binary_path(arch, binary_name):
if arch == 'aarch64':
arch = 'arm64'
script_dir = os.path.dirname(os.path.realpath(__file__))
arch_dir = os.path.join(script_dir, "bin", "ohos", arch)
if not os.path.isdir(arch_dir):
raise Exception("can't find arch directory: %s" % arch_dir)
binary_path = os.path.join(arch_dir, binary_name)
if not os.path.isfile(binary_path):
raise Exception("can't find binary: %s" % binary_path)
return binary_path
def str_to_bytes(str_value):
if not (sys.version_info >= (3, 0)):
return str_value
return str_value.encode('utf-8')
def bytes_to_str(bytes_value):
if not bytes_value:
return ''
if not (sys.version_info >= (3, 0)):
return bytes_value
return bytes_value.decode('utf-8')
def executable_file_available(executable, option='--help'):
try:
print([executable, option])
subproc = subprocess.Popen([executable, option],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
subproc.communicate(timeout=5)
return subproc.returncode == 0
except OSError:
return False
def dir_check(arg):
path = os.path.realpath(arg)
if not os.path.isdir(path):
raise argparse.ArgumentTypeError('{} is not a directory.'.format(path))
return path
def file_check(arg):
path = os.path.realpath(arg)
if not os.path.isfile(path):
raise argparse.ArgumentTypeError('{} is not a file.'.format(path))
return path
def get_arg_list(arg_list):
res = []
if arg_list:
for arg in arg_list:
res += arg
return res
def get_arch(arch):
if 'aarch64' in arch:
return 'arm64'
if 'arm' in arch:
return 'arm'
if 'x86_64' in arch or "amd64" in arch:
return 'x86_64'
if '86' in arch:
return 'x86'
raise Exception('unsupported architecture: %s' % arch.strip())
def find_tool_path(tool, tool_path=None):
if tool not in EXPECTED_TOOLS:
return None
tool_info = EXPECTED_TOOLS[tool]
test_option = tool_info.get('test_option', '--help')
path_in_tool = tool_info['path_in_tool']
path_in_tool = path_in_tool.replace('/', os.sep)
# 1. Find tool in the given tool path.
if tool_path:
path = os.path.join(tool_path, path_in_tool)
if executable_file_available(path, test_option):
return path
# 2. Find tool in the tool directory containing hiperf scripts.
path = os.path.join('../..', path_in_tool)
if executable_file_available(path, test_option):
return path
# 3. Find tool in $PATH.
if executable_file_available(tool, test_option):
return tool
return None
class HdcInterface:
def __init__(self, root_authority=True):
hdc_path = find_tool_path(HDC_NAME)
if not hdc_path:
raise Exception("Can't find hdc in PATH environment.")
self.hdc_path = hdc_path
self.root_authority = root_authority
def run_hdc_cmd(self, hdc_args, log_output=True):
hdc_args = [self.hdc_path] + hdc_args
if IS_DEBUG:
print('run hdc cmd: %s' % hdc_args)
subproc = subprocess.Popen(hdc_args, stdout=subprocess.PIPE)
(out, _) = subproc.communicate()
out = bytes_to_str(out)
return_code = subproc.returncode
result = (return_code == 0)
if out and hdc_args[1] != 'file send' and \
hdc_args[1] != 'file recv':
if log_output:
print(out)
if IS_DEBUG:
print('run hdc cmd: %s [result %s]' % (hdc_args, result))
return result, out
def check_run(self, hdc_args):
result, out = self.run_hdc_cmd(hdc_args)
if not result:
raise Exception('run "hdc %s" failed' % hdc_args)
return out
def _not_use_root(self):
result, out = self.run_hdc_cmd(['shell', 'whoami'])
if not result or 'root' not in out:
return
print('unroot hdc')
self.run_hdc_cmd(['unroot'])
time.sleep(1)
self.run_hdc_cmd(['wait-for-device'])
def switch_root(self):
if not self.root_authority:
self._not_use_root()
return False
result, out = self.run_hdc_cmd(['shell', 'whoami'])
if not result:
return False
if 'root' in out:
return True
build_type = self.get_attribute('ro.build.type')
if build_type == 'user':
return False
self.run_hdc_cmd(['root'])
time.sleep(1)
self.run_hdc_cmd(['wait-for-device'])
result, out = self.run_hdc_cmd(['shell', 'whoami'])
return result and 'root' in out
def get_attribute(self, name):
result, out = self.run_hdc_cmd(['shell', 'getprop',
name])
if result:
return out
def get_device_architecture(self):
output = self.check_run(['shell', 'uname', '-m'])
return get_arch(output)
class ElfStruct:
def __init__(self, path, lib_name):
self.path = path
self.name = lib_name
class LocalLibDownload:
def __init__(self, device_arch, hdc):
self.hdc = hdc
self.device_arch = device_arch
self.build_id_map_of_host = {}
self.build_id_map_of_device = {}
self.host_lib_count_map = {}
self.request_architectures = self.__get_need_architectures(device_arch)
def get_host_local_libs(self, local_lib_dir):
self.build_id_map_of_host.clear()
for root, dirs, files in os.walk(local_lib_dir):
for name in files:
if name.endswith('.so'):
self.__append_host_local_lib(os.path.join(root, name),
name)
def get_device_local_libs(self):
self.build_id_map_of_device.clear()
if os.path.exists(BUILD_ID_FILE):
remove(BUILD_ID_FILE)
self.hdc.check_run(['shell', 'mkdir', '-p', SYMBOL_FILES_DIR])
self.hdc.run_hdc_cmd(['file recv', SYMBOL_FILES_DIR + BUILD_ID_FILE])
if os.path.isfile(BUILD_ID_FILE):
with open(BUILD_ID_FILE, 'rb') as file_bin:
for line in file_bin.readlines():
line = bytes_to_str(line).strip()
# format is build_id = bin_name
items = line.split('=')
if len(items) == 2:
self.build_id_map_of_device[items[0]] = items[1]
remove(BUILD_ID_FILE)
def update_device_local_libs(self):
# Send local libs to device.
for build_id in self.build_id_map_of_host:
if build_id not in self.build_id_map_of_device:
elf_struct = self.build_id_map_of_host[build_id]
self.hdc.check_run(['file send', elf_struct.path,
SYMBOL_FILES_DIR + elf_struct.name])
# Remove Device lib while local libs not exist on host.
for build_id in self.build_id_map_of_device:
if build_id not in self.build_id_map_of_host:
name = self.build_id_map_of_device[build_id]
self.hdc.run_hdc_cmd(['shell', 'rm', SYMBOL_FILES_DIR + name])
# Send new build_id_list to device.
with open(BUILD_ID_FILE, 'wb') as file_bin:
for build_id in self.build_id_map_of_host:
str_bytes = str_to_bytes('%s=%s\n' % (build_id,
self.build_id_map_of_host[
build_id].name))
file_bin.write(str_bytes)
self.hdc.check_run(['file send', BUILD_ID_FILE,
SYMBOL_FILES_DIR + BUILD_ID_FILE])
remove(BUILD_ID_FILE)
def __append_host_local_lib(self, path, name):
build_id = get_build_id(path)
if not build_id:
return
arch = get_architecture(path)
if arch not in self.request_architectures:
return
elf_struct = self.build_id_map_of_host.get(build_id)
if not elf_struct:
count = self.host_lib_count_map.get(name, 0)
self.host_lib_count_map[name] = count + 1
if count == 0:
unique_name = name
else:
unique_name = name + '_' + count
self.build_id_map_of_host[build_id] = ElfStruct(path,
unique_name)
else:
elf_struct.path = path
def __get_need_architectures(self, device_arch):
if device_arch == 'x86_64':
return ['x86', 'x86_64']
if device_arch == 'x86':
return ['x86']
if device_arch == 'arm64':
return ['arm', 'arm64']
if device_arch == 'arm':
return ['arm']
return []
class PerformanceProfile:
"""Class of all Profilers."""
def __init__(self, args, control_module=""):
self.args = args
self.hdc = HdcInterface(root_authority=not args.not_hdc_root)
self.device_root = self.hdc.switch_root()
self.device_arch = self.hdc.get_device_architecture()
self.record_subproc = None
self.is_control = bool(control_module)
self.control_mode = control_module
def profile(self):
if not self.is_control or self.control_mode == "prepare":
print('prepare profiling')
self.download()
print('start profiling')
if not self.combine_args():
return
else:
self.exec_control()
self.profiling()
if not self.is_control:
print('pull profiling data')
self.get_profiling_data()
if self.control_mode == "stop":
self.wait_data_generate_done()
print('profiling is finished.')
def download(self):
"""Prepare recording. """
self.download_hiperf()
if self.args.local_lib_dir:
self.download_libs()
def download_hiperf(self):
hiperf_binary = get_hiperf_binary_path(self.device_arch, 'hiperf')
self.hdc.check_run(['file send', hiperf_binary, '/data/local/tmp'])
self.hdc.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/hiperf'])
def download_libs(self):
executor = LocalLibDownload(self.device_arch, self.hdc)
executor.get_host_local_libs(self.args.local_lib_dir)
executor.get_device_local_libs()
executor.update_device_local_libs()
def combine_args(self):
if self.args.package_name:
if self.args.ability:
self.kill_process()
self.start_profiling(['--app', self.args.package_name])
if self.args.ability:
ability = self.args.package_name + '/' + self.args.ability
start_cmd = ['shell', 'aa', 'start', '-a', ability]
result = self.hdc.run_hdc_cmd(start_cmd)
if not result:
self.record_subproc.terminate()
print("Can't start ability %s" % ability)
return False
# else: no need to start an ability.
elif self.args.local_program:
pid = self.hdc.check_run(['shell', 'pidof',
self.args.local_program])
if not pid:
print("Can't find pid of %s" % self.args.local_program)
return False
pid = int(pid)
self.start_profiling(['-p', str(pid)])
elif self.args.cmd:
cmds = self.args.cmd.split(' ')
cmd = [cmd.replace("'", "") for cmd in cmds]
self.start_profiling(cmd)
elif self.args.pid:
self.start_profiling(['-p', ','.join(self.args.pid)])
elif self.args.tid:
self.start_profiling(['-t', ','.join(self.args.tid)])
elif self.args.system_wide:
self.start_profiling(['-a'])
return True
def kill_process(self):
if self.get_app_process():
self.hdc.check_run(['shell', 'aa', 'force-stop', self.args.app])
count = 0
while True:
time.sleep(1)
pid = self.get_app_process()
if not pid:
break
count += 1
# 3 seconds exec kill
if count >= 3:
self.run_in_app_dir(['kill', '-9', str(pid)])
def get_app_process(self):
result, output = self.hdc.run_hdc_cmd(
['shell', 'pidof', self.args.package_name])
return int(output) if result else None
def run_in_app_dir(self, args):
if self.device_root:
hdc_args = ['shell', 'cd /data/data/' + self.args.package_name +
' && ' + (' '.join(args))]
else:
hdc_args = ['shell', 'run-as', self.args.package_name] + args
return self.hdc.run_hdc_cmd(hdc_args, log_output=False)
def start_profiling(self, selected_args):
"""Start hiperf reocrd process on device."""
record_options = self.args.record_options.split(' ')
record_options = [cmd.replace("'", "") for cmd in record_options]
if self.is_control:
args = ['/data/local/tmp/hiperf', 'record',
'--control', self.control_mode, '-o',
'/data/local/tmp/perf.data'] + record_options
else:
args = ['/data/local/tmp/hiperf', 'record', '-o',
'/data/local/tmp/perf.data'] + record_options
if self.args.local_lib_dir and self.hdc.run_hdc_cmd(
['shell', 'ls', SYMBOL_FILES_DIR]):
args += ['--symbol-dir', SYMBOL_FILES_DIR]
args += selected_args
hdc_args = [self.hdc.hdc_path, 'shell'] + args
print('run hdc cmd: %s' % hdc_args)
self.record_subproc = subprocess.Popen(hdc_args)
def profiling(self):
"""
Wait until profiling finishes, or stop profiling when user
presses Ctrl-C.
"""
try:
return_code = self.record_subproc.wait()
except KeyboardInterrupt:
self.end_profiling()
self.record_subproc = None
return_code = 0
print('profiling result [%s]' % (return_code == 0))
if return_code != 0:
raise Exception('Failed to record profiling data.')
def end_profiling(self):
"""
Stop profiling by sending SIGINT to hiperf, and wait until it exits
to make sure perf.data is completely generated.
"""
has_killed = False
while True:
(result, out) = self.hdc.run_hdc_cmd(
['shell', 'pidof', 'hiperf'])
if not out:
break
if not has_killed:
has_killed = True
self.hdc.run_hdc_cmd(['shell', 'pkill',
'-l', '2', 'hiperf'])
time.sleep(1)
def get_profiling_data(self):
current_path = os.getcwd()
full_path = os.path.join(current_path, self.args.output_perf_data)
self.hdc.check_run(['file recv', '/data/local/tmp/perf.data',
full_path])
self.hdc.run_hdc_cmd(['shell', 'rm',
'/data/local/tmp/perf.data'])
def exec_control(self):
hdc_args = [self.hdc.hdc_path, 'shell',
'/data/local/tmp/hiperf', 'record',
'--control', self.control_mode]
print('run hdc cmd: %s' % hdc_args)
self.record_subproc = subprocess.Popen(hdc_args)
def wait_data_generate_done(self):
last_size = 0
while True:
(result, out) = self.hdc.run_hdc_cmd(
['shell', 'du', 'data/local/tmp/perf.data'])
if "du" not in out:
current_size = out.split(" ")[0]
if current_size == last_size:
self.get_profiling_data()
break
else:
last_size = current_size
else:
print("not generate perf.data")
break
time.sleep(1)

77
script/loadlib_test.py Executable file
View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import sys
import os.path
from ctypes import *
from hiperf_utils import get_lib
libhiperf_report = get_lib()
if libhiperf_report is not None:
libhiperf_report.EchoLoopBack.restype = c_char_p
libhiperf_report.Report.restype = c_bool
libhiperf_report.Dump.restype = c_bool
libhiperf_report.ReportGetSymbolFiles.restype = c_char_p
libhiperf_report.ReportGetSymbolFiles.argtypes = [c_char_p]
libhiperf_report.ReportGetBuildId.restype = c_char_p
libhiperf_report.ReportGetElfArch.restype = c_char_p
ret = libhiperf_report.EchoLoopBack(b'123')
sys.stdout.flush()
print("\nfrom Python EchoLoopBack: ", ret)
ret = libhiperf_report.EchoLoopBack(b'test')
sys.stdout.flush()
print("\nfrom Python EchoLoopBack: ", ret)
ret = libhiperf_report.EchoLoopBack(b'test123')
sys.stdout.flush()
print("\nfrom Python EchoLoopBack: ", ret)
ret = libhiperf_report.EchoLoopBack(b'')
sys.stdout.flush()
print("\nfrom Python EchoLoopBack: ", ret)
ret = libhiperf_report.EchoLoopBack(None)
sys.stdout.flush()
print("\nfrom Python EchoLoopBack: ", ret)
ret = libhiperf_report.Report(b'perf.data', b'report.txt', b'-s')
print("\nfrom Python Report: ", ret)
ret = libhiperf_report.SetDebug(True)
print("\nfrom Python SetDebug: ", ret)
ret = libhiperf_report.ReportJson(b'perf.data', b'json.txt')
print("\nfrom Python ReportJson: ", ret)
ret = libhiperf_report.ReportGetSymbolFiles(b'perf.data')
print("\nfrom Python ReportGetSymbolFiles: ", ret)
ret = libhiperf_report.ReportGetBuildId(b'elf32_test')
print("\nfrom Python ReportGetBuildId: ", ret)
ret = libhiperf_report.ReportGetBuildId(b'elf32_test_stripped_nobuildid')
print("\nfrom Python ReportGetBuildId: ", ret)
ret = libhiperf_report.ReportGetElfArch(b'elf32_test')
print("\nfrom Python ReportGetElfArch: ", ret)
ret = libhiperf_report.ReportGetElfArch(b'elf_test')
print("\nfrom Python ReportGetElfArch: ", ret)
ret = libhiperf_report.ReportGetElfArch(b'hiperf')
print("\nfrom Python ReportGetElfArch: ", ret)

95
script/make_diff.py Executable file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import os
import sys
import time
import argparse
from hiperf_utils import get_lib
from hiperf_utils import dir_check
from hiperf_utils import file_check
def get_used_binaries(perf_data_base, perf_data_target, report_file,
local_lib_base, local_lib_target):
if local_lib_base:
get_lib().ReportUnwindJson(perf_data_base.encode("utf-8"),
'json_base.txt'.encode("utf-8"),
local_lib_base.encode("utf-8"))
else:
get_lib().ReportJson(perf_data_base.encode("utf-8"),
'json_base.txt'.encode("utf-8"))
if local_lib_target:
get_lib().ReportUnwindJson(perf_data_target.encode("utf-8"),
'json_target.txt'.encode("utf-8"),
local_lib_target.encode("utf-8"))
else:
get_lib().ReportJson(perf_data_target.encode("utf-8"),
'json_target.txt'.encode("utf-8"))
time.sleep(2)
with open('json_base.txt', 'r') as json_file:
all_json_base = json_file.read()
with open('json_target.txt', 'r') as json_file:
all_json_target = json_file.read()
with open('report-diff.html', 'r', encoding='utf-8') as html_file:
html_str = html_file.read()
with open(report_file, 'w', encoding='utf-8') as report_html_file:
combine_html(all_json_base, all_json_target)
report_html_file.write(html_str + combine_html(all_json_base, all_json_target))
dirname, _ = os.path.split(os.path.abspath(sys.argv[0]))
abs_path = os.path.join(dirname, report_file)
print("save to %s success" % abs_path)
os.remove("json_base.txt")
os.remove("json_target.txt")
def combine_html(json_1, json_2):
return '\n<script id = "record_data_diff_1" type = "application/json">' + \
json_1 + '</script>\n' \
'<script id = "record_data_diff_2" type = "application/json">' + \
json_2 + '</script> </body> </html>'
def main():
parser = argparse.ArgumentParser(description=""" To make a report, you
need to enter the data source and the path of the report.""")
parser.add_argument('-b', '--base', default='perf.data',
type=file_check, help=""" The path of base profiling
data.""")
parser.add_argument('-t', '--target', default='perf.data',
type=file_check, help=""" The path of target profiling
data.""")
parser.add_argument('-r', '--report_html', default='hiperf_report.html',
help="""the path of the report.""")
parser.add_argument('-lb', '--lib_base_dir', type=dir_check,
help="""Path to find symbol dir use to
base data offline unwind stack""")
parser.add_argument('-lt', '--lib_target_dir', type=dir_check,
help="""Path to find symbol dir use to
target data offline unwind stack""")
args = parser.parse_args()
get_used_binaries(args.base,args.target, args.report_html,
args.lib_base_dir, args.lib_target_dir)
if __name__ == '__main__':
main()

65
script/make_report.py Normal file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import os
import sys
import time
import argparse
from hiperf_utils import get_lib
from hiperf_utils import dir_check
from hiperf_utils import file_check
def get_used_binaries(perf_data, report_file, local_lib_dir):
if local_lib_dir:
get_lib().ReportUnwindJson(perf_data.encode("utf-8"),
'json.txt'.encode("utf-8"),
local_lib_dir.encode("utf-8"))
else:
get_lib().ReportJson(perf_data.encode("utf-8"),
'json.txt'.encode("utf-8"))
time.sleep(2)
with open('json.txt', 'r') as json_file:
all_json = json_file.read()
with open('report.html', 'r', encoding='utf-8') as html_file:
html_str = html_file.read()
with open(report_file, 'w', encoding='utf-8') as report_html_file:
report_html_file.write(html_str + all_json + '</script>'
' </body>'
' </html>')
dirname, _ = os.path.split(os.path.abspath(sys.argv[0]))
abs_path = os.path.join(dirname, report_file)
print("save to %s success" % abs_path)
os.remove('json.txt')
def main():
parser = argparse.ArgumentParser(description=""" To make a report, you
need to enter the data source and the path of the report.""")
parser.add_argument('-i', '--perf_data', default='perf.data',
type=file_check, help=""" The path of profiling
data.""")
parser.add_argument('-r', '--report_html', default='hiperf_report.html',
help="""the path of the report.""")
parser.add_argument('-l', '--local_lib_dir', type=dir_check,
help="""Path to find symbol dir use to
do offline unwind stack""")
args = parser.parse_args()
get_used_binaries(args.perf_data, args.report_html, args.local_lib_dir)
if __name__ == '__main__':
main()

60
script/make_report_sample.py Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import os
import sys
import time
import argparse
from hiperf_utils import get_lib
from hiperf_utils import dir_check
from hiperf_utils import file_check
def get_used_binaries(perf_data, command):
get_lib().Report(perf_data.encode("utf-8"),
'report.txt'.encode("utf-8"), command.encode())
with open('report.txt', 'r') as report_file:
print(report_file.read())
dirname, _ = os.path.split(os.path.abspath(sys.argv[0]))
abs_path = os.path.join(dirname, 'report.txt')
print("save to %s success" % abs_path)
def main():
parser = argparse.ArgumentParser(description=""" To make a report, you
need to enter the data source and the path of the report.""")
parser.add_argument('-i', '--perf_data', default='perf.data',
type=file_check, help=""" The path of profiling
data.""")
parser.add_argument('-l', '--local_lib_dir', type=dir_check,
help="""Path to find symbol dir use to
do offline unwind stack""")
parser.add_argument('-s', '--call_stack', action='store_true',
help="""print the call_stack""")
args = parser.parse_args()
command = ""
if args.call_stack:
command += "-s "
if args.local_lib_dir:
command += "--symbol-dir " + args.local_lib_dir
get_used_binaries(args.perf_data, command)
if __name__ == '__main__':
main()

54
script/package.sh Executable file
View File

@ -0,0 +1,54 @@
#!/bin/bash
# Copyright (c) 2021 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.
set -e
DIR=$(dirname $(realpath ${BASH_SOURCE[0]}))
TOP=$(realpath $DIR/../../../)
echo $DIR
cd $DIR
BUILD_OUT=$TOP/out/ohos-arm-release
HOST_OUT=$TOP/out/host/developtools/hiperf
BUILD_PATH=/developtools/hiperf
rm -rf $HOST_OUT
mkdir -p $HOST_OUT
# script
cp -vf *.py $HOST_OUT
cp -vf *.html $HOST_OUT
# bin
HOST_LINUX_PATH=$BUILD_OUT/clang_x64$BUILD_PATH
HOST_LINUX_OUT_PATH=$HOST_OUT/bin/linux/x86_64
mkdir -p $HOST_LINUX_OUT_PATH
HOST_WINDOWS_PATH=$BUILD_OUT/mingw_x86_64$BUILD_PATH
HOST_WINDOWS_OUT_PATH=$HOST_OUT/bin/windows/x86_64
mkdir -p $HOST_WINDOWS_OUT_PATH
OHOS_ARM_PATH=$BUILD_OUT$BUILD_PATH
OHOS_ARM_OUT_PATH=$HOST_OUT/bin/ohos/arm
mkdir -p $OHOS_ARM_OUT_PATH
cp -vf $HOST_LINUX_PATH/hiperf_host $HOST_LINUX_OUT_PATH
cp -vf $HOST_LINUX_PATH/*.so $HOST_LINUX_OUT_PATH
cp -vf $HOST_WINDOWS_PATH/hiperf_host.exe $HOST_WINDOWS_OUT_PATH
cp -vf $HOST_WINDOWS_PATH/*.dll $HOST_WINDOWS_OUT_PATH
cp -vf $OHOS_ARM_PATH/hiperf $OHOS_ARM_OUT_PATH
cd $HOST_OUT/../
rm -f hiperf.tar.gz
tar czvf hiperf.tar.gz hiperf
cd -

153
script/record_control.py Executable file
View File

@ -0,0 +1,153 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 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.
import os
import time
import argparse
import subprocess
from hiperf_utils import HdcInterface
from hiperf_utils import get_build_id
from hiperf_utils import get_architecture
from hiperf_utils import PerformanceProfile
from hiperf_utils import dir_check
from hiperf_utils import get_hiperf_binary_path
from hiperf_utils import bytes_to_str
from hiperf_utils import str_to_bytes
from hiperf_utils import remove
def check_args(args):
if (not args.package_name) and args.ability:
raise Exception('-a can only be used when profiling an OHOS '
'package_name.')
return True
def get_module(args):
if args.prepare:
return "prepare"
elif args.start:
return "start"
elif args.pause:
return "pause"
elif args.resume:
return "resume"
elif args.stop:
return "stop"
def parser_add_argument():
description = "Collect performance sampling information of" \
" running [command]."
parser = argparse.ArgumentParser(description=description)
control_group = parser.add_argument_group(
'Select Control options').add_mutually_exclusive_group(required=True)
control_group.add_argument('--prepare', action='store_true',
help='prepare need to add profiling target to '
'execute record .'
'Like --prepare -p 121')
control_group.add_argument('--start', action='store_true',
help='start execute hiperf record')
control_group.add_argument('--pause', action='store_true',
help='pause execute hiperf record')
control_group.add_argument('--resume', action='store_true',
help='pause execute hiperf record')
control_group.add_argument('--stop', action='store_true',
help='stop execute hiperf record'
'and file recv data file to local')
target_group = parser.add_argument_group(title='Select profiling target') \
.add_mutually_exclusive_group(required=False)
target_group.add_argument('-app', '--package_name',
help="""Collect profile info for an OHOS app""")
target_group.add_argument('-lp', '--local_program',
help="""Collect profile info
for an local program.""")
target_group.add_argument('-cmd',
help="""Running a command on the OHOS device.
like as : -cmd "'ps -ef'".
the ps will open as child process of hiperf
and sample this process.""")
target_group.add_argument('-p', '--pid', nargs='*',
help="""Limit the process id of the collection
target.""")
target_group.add_argument('-t', '--tid', nargs='*',
help="""Limit the thread id of the collection
target.""")
target_group.add_argument('-sw', '--system_wide', action='store_true',
help="""Collect system-wide information.
This requires CAP_PERFMON (since Linux 5.8) or
CAP_SYS_ADMIN capability or a
/proc/sys/kernel/perf_event_paranoid
value of less than 1.""")
record_group = parser.add_argument_group('Select recording options')
record_group.add_argument('-a', '--ability',
help="""Used with -p. Profile the launch time of
an ability in an OHOS app. The app will be started or
restarted to run the ability.
Like : -a .MainAbility """)
record_group.add_argument('-r', '--record_options',
default='-f 1000 -s dwarf',
help="""Set recording options for `hiperf record`
command. Default is "'-f 1000 -s dwarf'".""")
record_group.add_argument('-lib', '--local_lib_dir', type=dir_check,
help="""When profiling an OHOS app containing
local thelocal libraries are usually stripped and lake
of symbols and debug information to provide good
profiling result. By using -lib, you tell
command_script.py the path storing unstripped local
libraries, and script will search all shared libraries
with suffix .so in the directory. Then the local
libraries will be downloaded on device and collected
in build_cache.""")
record_group.add_argument('-o', '--output_perf_data', default='perf.data',
help='The path to store profiling data. '
'Default is perf.data.')
other_group = parser.add_argument_group('Other options')
other_group.add_argument('--not_hdc_root', action='store_true',
help="""Force hdc to run in non root mode. """)
args = parser.parse_args()
return args
def main(args):
check_args(args)
args_mark = [args.package_name, args.local_program, args.cmd,
args.pid, args.tid, args.system_wide]
any_args = any(args_mark)
if args.prepare and not any_args:
print("prepare need one of -app/-lp/-cmd/-p/-t/-sw "
"argument to execute record")
return False
profiler = PerformanceProfile(args, control_module=get_module(args))
profiler.profile()
return True
if __name__ == '__main__':
main(parser_add_argument())

Some files were not shown because too many files have changed in this diff Show More