mirror of
https://gitee.com/openharmony/developtools_hiperf
synced 2024-11-26 17:21:15 +00:00
add hiperf
Signed-off-by: chuxuezhe11 <hanjixiao@huawei.com>
This commit is contained in:
parent
cb616052f3
commit
eefcd9d075
10
.gitignore
vendored
Executable file
10
.gitignore
vendored
Executable 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
476
BUILD.gn
Normal 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
201
LICENSE
Normal 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
109
OAT.xml
Executable 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:
|
||||
" == >
|
||||
& == >
|
||||
' == >
|
||||
< == >
|
||||
> == >
|
||||
-->
|
||||
<configuration>
|
||||
<oatconfig>
|
||||
<filefilterlist>
|
||||
<filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license 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>
|
36
README.en.md
36
README.en.md
@ -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/)
|
39
README.md
39
README.md
@ -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
407
README_zh.md
Executable file
File diff suppressed because one or more lines are too long
29
demo/cpp/BUILD.gn
Normal file
29
demo/cpp/BUILD.gn
Normal 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
93
demo/cpp/hiperf_demo.cpp
Normal 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
483
demo/cpp/hiperf_example_cmd.cpp
Executable 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
41
demo/cpp/hiperf_malloc_demo.cpp
Executable 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
16
demo/js/.gitignore
vendored
Executable 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
59
demo/js/build.gradle
Normal 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
2
demo/js/entry/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
/node_modules
|
21
demo/js/entry/build.gradle
Normal file
21
demo/js/entry/build.gradle
Normal 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
1
demo/js/entry/package.json
Executable file
@ -0,0 +1 @@
|
||||
{}
|
1
demo/js/entry/proguard-rules.pro
vendored
Executable file
1
demo/js/entry/proguard-rules.pro
vendored
Executable file
@ -0,0 +1 @@
|
||||
# config module specific ProGuard rules here.
|
63
demo/js/entry/src/main/config.json
Normal file
63
demo/js/entry/src/main/config.json
Normal 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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
8
demo/js/entry/src/main/js/MainAbility/app.js
Executable file
8
demo/js/entry/src/main/js/MainAbility/app.js
Executable file
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
onCreate() {
|
||||
console.info("Application onCreate");
|
||||
},
|
||||
onDestroy() {
|
||||
console.info("Application onDestroy");
|
||||
}
|
||||
};
|
15
demo/js/entry/src/main/js/MainAbility/i18n/en-US.json
Executable file
15
demo/js/entry/src/main/js/MainAbility/i18n/en-US.json
Executable 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": {
|
||||
}
|
||||
}
|
15
demo/js/entry/src/main/js/MainAbility/i18n/zh-CN.json
Executable file
15
demo/js/entry/src/main/js/MainAbility/i18n/zh-CN.json
Executable file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"strings": {
|
||||
"hello": "您好",
|
||||
"world": "世界",
|
||||
"page": "第二页",
|
||||
"next": "下一页",
|
||||
"back": "返回",
|
||||
"test_record": "测试 record",
|
||||
"test_option": "测试 option",
|
||||
"test_click": "测试 点击",
|
||||
"output": "测试输出"
|
||||
},
|
||||
"Files": {
|
||||
}
|
||||
}
|
39
demo/js/entry/src/main/js/MainAbility/pages/index/index.css
Executable file
39
demo/js/entry/src/main/js/MainAbility/pages/index/index.css
Executable 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;
|
||||
}
|
21
demo/js/entry/src/main/js/MainAbility/pages/index/index.hml
Executable file
21
demo/js/entry/src/main/js/MainAbility/pages/index/index.hml
Executable 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>
|
170
demo/js/entry/src/main/js/MainAbility/pages/index/index.js
Executable file
170
demo/js/entry/src/main/js/MainAbility/pages/index/index.js
Executable 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
|
||||
}
|
||||
}
|
24
demo/js/entry/src/main/js/MainAbility/pages/second/second.css
Executable file
24
demo/js/entry/src/main/js/MainAbility/pages/second/second.css
Executable 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;
|
||||
}
|
6
demo/js/entry/src/main/js/MainAbility/pages/second/second.hml
Executable file
6
demo/js/entry/src/main/js/MainAbility/pages/second/second.hml
Executable 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>
|
12
demo/js/entry/src/main/js/MainAbility/pages/second/second.js
Executable file
12
demo/js/entry/src/main/js/MainAbility/pages/second/second.js
Executable file
@ -0,0 +1,12 @@
|
||||
import router from '@system.router'
|
||||
|
||||
export default {
|
||||
data: {
|
||||
title: 'World'
|
||||
},
|
||||
onclick: function () {
|
||||
router.replace({
|
||||
uri: "pages/index/index"
|
||||
})
|
||||
}
|
||||
}
|
12
demo/js/entry/src/main/resources/base/element/string.json
Normal file
12
demo/js/entry/src/main/resources/base/element/string.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "entry_MainAbility",
|
||||
"value": "HiperfDemo"
|
||||
},
|
||||
{
|
||||
"name": "description_mainability",
|
||||
"value": "Hiperf Demo API JS"
|
||||
}
|
||||
]
|
||||
}
|
BIN
demo/js/entry/src/main/resources/base/media/icon.png
Normal file
BIN
demo/js/entry/src/main/resources/base/media/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
1
demo/js/package.json
Executable file
1
demo/js/package.json
Executable file
@ -0,0 +1 @@
|
||||
{}
|
15
demo/js/settings.gradle
Normal file
15
demo/js/settings.gradle
Normal 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'
|
29
demo/js/signature/demo.cer
Normal file
29
demo/js/signature/demo.cer
Normal 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-----
|
9
demo/js/signature/demo.csr
Normal file
9
demo/js/signature/demo.csr
Normal 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
BIN
demo/js/signature/demo.p12
Normal file
Binary file not shown.
BIN
demo/js/signature/demo.p7b
Normal file
BIN
demo/js/signature/demo.p7b
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
.~猥<>3メV1Rィiノ"
|
BIN
demo/js/signature/material/ce/c2058a747a5c4cadbe2dd70a487e46b5
Normal file
BIN
demo/js/signature/material/ce/c2058a747a5c4cadbe2dd70a487e46b5
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
̒Ln~3<02><>9<EFBFBD><39><EFBFBD>
|
@ -0,0 +1 @@
|
||||
áKľ‘ŃDho@ş;ćT9
|
@ -0,0 +1 @@
|
||||
乏ク=(rX婀エ_
|
72
hiperf.gni
Normal file
72
hiperf.gni
Normal 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"]
|
||||
}
|
67
include/arm_raw_event_type_table.h
Normal file
67
include/arm_raw_event_type_table.h
Normal 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
139
include/callstack.h
Executable 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
32
include/command.h
Executable 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
371
include/debug_logger.h
Executable 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
193
include/dwarf_encoding.h
Executable 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
310
include/elf_parser.h
Executable 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
225
include/hashlist.h
Executable 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
1000
include/hashlist.hpp
Executable file
File diff suppressed because it is too large
Load Diff
95
include/hiperf_hilog.h
Normal file
95
include/hiperf_hilog.h
Normal 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
|
98
include/hiperf_libreport.h
Normal file
98
include/hiperf_libreport.h
Normal 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
90
include/mem_map_item.h
Executable 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
26
include/noncopyable.h
Normal 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
|
25
include/nonlinux/MingW64Fix.h
Normal file
25
include/nonlinux/MingW64Fix.h
Normal 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
|
17
include/nonlinux/asm/byteorder.h
Normal file
17
include/nonlinux/asm/byteorder.h
Normal 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
|
17
include/nonlinux/linux/ioctl.h
Normal file
17
include/nonlinux/linux/ioctl.h
Normal 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
|
33
include/nonlinux/linux/types.h
Normal file
33
include/nonlinux/linux/types.h
Normal 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
140
include/option.h
Executable 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
30
include/option_debug.h
Executable 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
435
include/perf_event_record.h
Executable 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
463
include/perf_events.h
Executable 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
259
include/perf_file_format.h
Executable 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
102
include/perf_file_reader.h
Executable 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
86
include/perf_file_writer.h
Executable 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
360
include/perf_record_format.h
Executable 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
193
include/register.h
Executable 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
573
include/report.h
Executable 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
379
include/report_json_file.h
Executable 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
100
include/report_protobuf_file.h
Executable 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
54
include/ring_buffer.h
Executable 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
102
include/subcommand.h
Executable 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
118
include/subcommand_dump.h
Normal 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 §ionEventdesc);
|
||||
VirtualRuntime vr_;
|
||||
};
|
||||
} // namespace HiPerf
|
||||
} // namespace Developtools
|
||||
} // namespace OHOS
|
||||
#endif
|
45
include/subcommand_help.h
Normal file
45
include/subcommand_help.h
Normal 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
69
include/subcommand_list.h
Normal 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
306
include/subcommand_record.h
Normal 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
191
include/subcommand_report.h
Normal 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
124
include/subcommand_stat.h
Normal 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
345
include/symbols_file.h
Executable 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 §ionVaddr,
|
||||
[[maybe_unused]] uint64_t §ionSize,
|
||||
[[maybe_unused]] uint64_t §ionFileOffset) 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
80
include/tracked_command.h
Executable 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
321
include/utilities.h
Executable 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
179
include/virtual_runtime.h
Executable 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
113
include/virtual_thread.h
Executable 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
|
41
interfaces/innerkits/native/BUILD.gn
Normal file
41
interfaces/innerkits/native/BUILD.gn
Normal 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"
|
||||
}
|
326
interfaces/innerkits/native/include/hiperf_client.h
Normal file
326
interfaces/innerkits/native/include/hiperf_client.h
Normal 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 don’t 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
|
565
interfaces/innerkits/native/src/hiperf_client.cpp
Normal file
565
interfaces/innerkits/native/src/hiperf_client.cpp
Normal 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
249
interfaces/kits/js/@ohos.hiperf.d.ts
vendored
Normal 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 don’t 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;
|
44
interfaces/kits/js/napi/BUILD.gn
Normal file
44
interfaces/kits/js/napi/BUILD.gn
Normal 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"
|
||||
}
|
688
interfaces/kits/js/napi/hiperf_client_napi.cpp
Normal file
688
interfaces/kits/js/napi/hiperf_client_napi.cpp
Normal 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);
|
||||
}
|
18
interfaces/kits/js/napi/hiperf_client_napi.h
Executable file
18
interfaces/kits/js/napi/hiperf_client_napi.h
Executable 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
27
ohos.build
Normal 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
36
proto/build_proto.sh
Executable 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
91
proto/report_sample.proto
Normal 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
114
script/command_script.py
Normal 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
553
script/hiperf_utils.py
Normal 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
77
script/loadlib_test.py
Executable 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
95
script/make_diff.py
Executable 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
65
script/make_report.py
Normal 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
60
script/make_report_sample.py
Executable 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
54
script/package.sh
Executable 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
153
script/record_control.py
Executable 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
Loading…
Reference in New Issue
Block a user