From 15a8d79bd9163fc6e05537ddb86a1e7a02833397 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 12 Jan 2024 13:43:24 +0000 Subject: Add kcmdlinectrl for setting the kcmdline bootloader message This utility makes it possible to read and write to the kcmdline bootloader message. This message can be used to tell the bootloader to set kcmdline flags for enabling features in the kernel. Test: Verified that a custom bootloader is able to read the data Bug: 278052745 Change-Id: I5f13a9bdff940517cb7b880815dfb8f396fc3844 Signed-off-by: Alice Ryhl --- kcmdlinectrl/.clang-format | 1 + kcmdlinectrl/Android.bp | 26 +++++++++++++ kcmdlinectrl/OWNERS | 1 + kcmdlinectrl/kcmdlinectrl.cc | 92 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 120000 kcmdlinectrl/.clang-format create mode 100644 kcmdlinectrl/Android.bp create mode 100644 kcmdlinectrl/OWNERS create mode 100644 kcmdlinectrl/kcmdlinectrl.cc diff --git a/kcmdlinectrl/.clang-format b/kcmdlinectrl/.clang-format new file mode 120000 index 00000000..242a033c --- /dev/null +++ b/kcmdlinectrl/.clang-format @@ -0,0 +1 @@ +../../core/.clang-format-2 \ No newline at end of file diff --git a/kcmdlinectrl/Android.bp b/kcmdlinectrl/Android.bp new file mode 100644 index 00000000..37ef1473 --- /dev/null +++ b/kcmdlinectrl/Android.bp @@ -0,0 +1,26 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary { + name: "kcmdlinectrl", + srcs: ["kcmdlinectrl.cc"], + shared_libs: [ + "libbootloader_message", + "libbase", + ], +} diff --git a/kcmdlinectrl/OWNERS b/kcmdlinectrl/OWNERS new file mode 100644 index 00000000..ad8cee6c --- /dev/null +++ b/kcmdlinectrl/OWNERS @@ -0,0 +1 @@ +aliceryhl@google.com diff --git a/kcmdlinectrl/kcmdlinectrl.cc b/kcmdlinectrl/kcmdlinectrl.cc new file mode 100644 index 00000000..48500d86 --- /dev/null +++ b/kcmdlinectrl/kcmdlinectrl.cc @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +void PrintUsage(const char* progname) { + std::cerr << "USAGE: " << progname << " PROPERTY_NAME [VALUE]" << endl; +} + +int main(int argc, char** argv) { + char *property_name, *new_value; + if (argc == 2) { + // Read property. + property_name = argv[1]; + new_value = NULL; + } else if (argc == 3) { + // Write property. + property_name = argv[1]; + new_value = argv[2]; + } else { + PrintUsage(*argv); + return 1; + } + + misc_kcmdline_message m = {.version = MISC_KCMDLINE_MESSAGE_VERSION, + .magic = MISC_KCMDLINE_MAGIC_HEADER}; + + std::string err; + if (!ReadMiscKcmdlineMessage(&m, &err)) { + LOG(ERROR) << "Failed to read from misc: " << err << endl; + return 1; + } + + if (m.magic != MISC_KCMDLINE_MAGIC_HEADER || m.version != MISC_KCMDLINE_MESSAGE_VERSION) { + cout << "kcmdline message is invalid, resetting it" << endl; + m = {.version = MISC_KCMDLINE_MESSAGE_VERSION, + .magic = MISC_KCMDLINE_MAGIC_HEADER, + .kcmdline_flags = 0}; + } + + if (!strcmp(property_name, "binder")) { + if (new_value == NULL) { + bool use_rust_binder = (m.kcmdline_flags & MISC_KCMDLINE_BINDER_RUST) != 0; + const char* binder_value = use_rust_binder ? "rust" : "c"; + cout << "binder=" << binder_value << endl; + return 0; + } else if (!strcmp(new_value, "rust")) { + m.kcmdline_flags |= MISC_KCMDLINE_BINDER_RUST; + } else if (!strcmp(new_value, "c")) { + m.kcmdline_flags &= !MISC_KCMDLINE_BINDER_RUST; + } else { + LOG(ERROR) << "Binder property can only by 'c' or 'rust', but got " << new_value << endl; + return 1; + } + } else { + LOG(ERROR) << "No such property name " << property_name << endl; + return 1; + } + + if (!WriteMiscKcmdlineMessage(m, &err)) { + LOG(ERROR) << "Failed to write to misc: " << err << endl; + return 1; + } + + return 0; +} -- cgit v1.2.3 From bbe9f034ab0be65bca43429c6aee5f6c14fb95a9 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 29 Jan 2024 16:14:05 -0800 Subject: simpleperf: gecko_profile_generator.py: Add --percpu-samples It is to show sample distribution for each cpu: 1) Use cpu ids as thread names. 2) In each sample, add thread name + process name as the root of the call stack. Bug: 323605908 Test: run test.py --only-host-test Change-Id: Ie6e363727069876d8093bedd93c37daeb0a76cf0 --- simpleperf/scripts/gecko_profile_generator.py | 72 +++++++++++++++++++-------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/simpleperf/scripts/gecko_profile_generator.py b/simpleperf/scripts/gecko_profile_generator.py index d09a00b1..980012c0 100755 --- a/simpleperf/scripts/gecko_profile_generator.py +++ b/simpleperf/scripts/gecko_profile_generator.py @@ -390,7 +390,8 @@ def _gecko_profile( symfs_dir: Optional[str], kallsyms_file: Optional[str], report_lib_options: ReportLibOptions, - max_remove_gap_length: int) -> GeckoProfile: + max_remove_gap_length: int, + percpu_samples: bool) -> GeckoProfile: """convert a simpleperf profile to gecko format""" lib = GetReportLib(record_file) @@ -399,6 +400,10 @@ def _gecko_profile( lib.SetSymfs(symfs_dir) if kallsyms_file is not None: lib.SetKallsymsFile(kallsyms_file) + if percpu_samples: + # Grouping samples by cpus doesn't support off cpu samples. + if lib.GetSupportedTraceOffCpuModes(): + report_lib_options.trace_offcpu = 'on-cpu' lib.SetReportOptions(report_lib_options) arch = lib.GetArch() @@ -406,7 +411,9 @@ def _gecko_profile( record_cmd = lib.GetRecordCmd() # Map from tid to Thread - threadMap: Dict[int, Thread] = {} + thread_map: Dict[int, Thread] = {} + # Map from pid to process name + process_names: Dict[int, str] = {} while True: sample = lib.GetNextSample() @@ -424,28 +431,44 @@ def _gecko_profile( # We want root first, leaf last. stack.reverse() - # add thread sample - thread = threadMap.get(sample.tid) - if thread is None: - thread = Thread(comm=sample.thread_comm, pid=sample.pid, tid=sample.tid) - threadMap[sample.tid] = thread - thread.add_sample( - comm=sample.thread_comm, - stack=stack, - # We are being a bit fast and loose here with time here. simpleperf - # uses CLOCK_MONOTONIC by default, which doesn't use the normal unix - # epoch, but rather some arbitrary time. In practice, this doesn't - # matter, the Firefox Profiler normalises all the timestamps to begin at - # the minimum time. Consider fixing this in future, if needed, by - # setting `simpleperf record --clockid realtime`. - time_ms=sample_time_ms) - - for thread in threadMap.values(): + if percpu_samples: + if sample.tid == sample.pid: + process_names[sample.pid] = sample.thread_comm + process_name = process_names.get(sample.pid) + stack = [ + '%s tid %d (in %s pid %d)' % + (sample.thread_comm, sample.tid, process_name, sample.pid)] + stack + thread = thread_map.get(sample.cpu) + if thread is None: + thread = Thread(comm=f'Cpu {sample.cpu}', pid=sample.cpu, tid=sample.cpu) + thread_map[sample.cpu] = thread + thread.add_sample( + comm=f'Cpu {sample.cpu}', + stack=stack, + time_ms=sample_time_ms) + else: + # add thread sample + thread = thread_map.get(sample.tid) + if thread is None: + thread = Thread(comm=sample.thread_comm, pid=sample.pid, tid=sample.tid) + thread_map[sample.tid] = thread + thread.add_sample( + comm=sample.thread_comm, + stack=stack, + # We are being a bit fast and loose here with time here. simpleperf + # uses CLOCK_MONOTONIC by default, which doesn't use the normal unix + # epoch, but rather some arbitrary time. In practice, this doesn't + # matter, the Firefox Profiler normalises all the timestamps to begin at + # the minimum time. Consider fixing this in future, if needed, by + # setting `simpleperf record --clockid realtime`. + time_ms=sample_time_ms) + + for thread in thread_map.values(): thread.sort_samples() - remove_stack_gaps(max_remove_gap_length, threadMap) + remove_stack_gaps(max_remove_gap_length, thread_map) - threads = [thread.to_json_dict() for thread in threadMap.values()] + threads = [thread.to_json_dict() for thread in thread_map.values()] profile_timestamp = meta_info.get('timestamp') end_time_ms = (int(profile_timestamp) * 1000) if profile_timestamp else 0 @@ -504,6 +527,9 @@ def main() -> None: broken-stack samples we want to remove. """ ) + parser.add_argument( + '--percpu-samples', action='store_true', + help='show samples based on cpus instead of threads') parser.add_report_lib_options() args = parser.parse_args() profile = _gecko_profile( @@ -511,7 +537,9 @@ def main() -> None: symfs_dir=args.symfs, kallsyms_file=args.kallsyms, report_lib_options=args.report_lib_options, - max_remove_gap_length=args.max_remove_gap_length) + max_remove_gap_length=args.max_remove_gap_length, + percpu_samples=args.percpu_samples, + ) json.dump(profile, sys.stdout, sort_keys=True) -- cgit v1.2.3 From 757d924df31ebeb0807b5ab8bc7815dfdd581320 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 6 Feb 2024 00:25:15 +0000 Subject: memeater: moved into toybox. Note that the exact toybox equivalent of `memeater 2` is `nohup memeater 2m` (other suffixes like `k` or `g` work too, and `nohup` lets you run any command in a way that's immune to being killed when its controlling terminal goes away). Test: treehugger Change-Id: Ibd0073e358692bbe9b13bd4785380d61f2bdd752 --- tests/memeater/Android.bp | 38 ---------- tests/memeater/NOTICE | 190 ---------------------------------------------- tests/memeater/memeater.c | 107 -------------------------- 3 files changed, 335 deletions(-) delete mode 100644 tests/memeater/Android.bp delete mode 100644 tests/memeater/NOTICE delete mode 100644 tests/memeater/memeater.c diff --git a/tests/memeater/Android.bp b/tests/memeater/Android.bp deleted file mode 100644 index dfeaea70..00000000 --- a/tests/memeater/Android.bp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// 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. -// Copyright The Android Open Source Project - -package { - // See: http://go/android-license-faq - default_applicable_licenses: ["system_extras_tests_memeater_license"], -} - -license { - name: "system_extras_tests_memeater_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - -cc_binary { - name: "memeater", - srcs: ["memeater.c"], - cflags: [ - "-Wno-unused-parameter", - ], -} diff --git a/tests/memeater/NOTICE b/tests/memeater/NOTICE deleted file mode 100644 index 25f8ab95..00000000 --- a/tests/memeater/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2017, The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - 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. - - - 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 - diff --git a/tests/memeater/memeater.c b/tests/memeater/memeater.c deleted file mode 100644 index b2e0b269..00000000 --- a/tests/memeater/memeater.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * 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. - * - * Simple memory eater. Runs as a daemon. Prints the child PID to - * std so you can easily kill it later. - * Usage: memeater - */ - -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - pid_t pid; - int fd; - int numMb = argc > 1 ? atoi(argv[1]) : 1; - - if (argc < 2) { - printf("Usage: memeater \n"); - exit(1); - } - - switch (fork()) { - case -1: - perror(argv[0]); - exit(1); - break; - case 0: /* child */ - chdir("/"); - umask(0); - setpgrp(); - setsid(); - /* fork again to fully detach from controlling terminal. */ - switch (pid = fork()) { - case -1: - perror("failed to fork"); - break; - case 0: /* second child */ - /* redirect to /dev/null */ - close(0); - open("/dev/null", 0); - for (fd = 3; fd < 256; fd++) { - close(fd); - } - - printf("Allocating %d MB\n", numMb); - fflush(stdout); - /* allocate memory and fill it */ - while (numMb > 0) { - // Allocate 500MB at a time at most - int mbToAllocate = numMb > 500 ? 500 : numMb; - int bytesToAllocate = mbToAllocate * 1024 * 1024; - char *p = malloc(bytesToAllocate); - if (p == NULL) { - printf("Failed to allocate memory\n"); - exit(1); - } - for (int j = 0; j < bytesToAllocate; j++) { - p[j] = j & 0xFF; - } - printf("Allocated %d MB %p\n", mbToAllocate, p); - fflush(stdout); - numMb -= mbToAllocate; - } - - close(1); - if (open("/dev/null", O_WRONLY) < 0) { - perror("/dev/null"); - exit(1); - } - close(2); - dup(1); - - /* Sit around doing nothing */ - while (1) { - usleep(1000000); - } - default: - /* so caller can easily kill it later. */ - printf("%d\n", pid); - exit(0); - break; - } - break; - default: - exit(0); - break; - } - return 0; -} - -- cgit v1.2.3 From 40a51f76d7d6e315f2c66668e9234fc2faee85c4 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Fri, 2 Feb 2024 17:18:57 -0800 Subject: simpleperf: report_html.py: split disassemble work evenly Currently, report_html.py decides how to disassemble functions by checking how many functions need to be disassembled in a binary: 1) If not many, disassemble the functions one by one. 2) Otherwise, disassemble the whole binary. However, the samples may hit many functions in a large binary (like libmonochrome_64.so), while they only take a small percentage of the binary. And the time used to disassemble the whole binary can be longer than disassembling all other files. To speed it up, we can split the large binary into multiple parts, each taking 1M code size, and disassemble needed parts on different CPUs. In an experiment disassembling chrome profile, the disassembling time is reduced from 3 minutes to 2 minutes. The time is printed by adding `--log debug`. Also add `--disassemble-job-size` to adjust code split size. Bug: 323271419 Test: run report_html.py manually Test: run test.py --only-host-test Change-Id: I6af546616cbca7e294956b610182757b679fcb04 --- simpleperf/scripts/report_html.py | 64 +++++++++++++++++++++------------- simpleperf/scripts/simpleperf_utils.py | 9 ++++- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/simpleperf/scripts/report_html.py b/simpleperf/scripts/report_html.py index 368bd509..ba143fd0 100755 --- a/simpleperf/scripts/report_html.py +++ b/simpleperf/scripts/report_html.py @@ -801,7 +801,8 @@ class RecordData(object): # Collect needed source code in SourceFileSet. self.source_files.load_source_code(source_dirs) - def add_disassembly(self, filter_lib: Callable[[str], bool], jobs: int): + def add_disassembly(self, filter_lib: Callable[[str], bool], + jobs: int, disassemble_job_size: int): """ Collect disassembly information: 1. Use objdump to collect disassembly for each function in FunctionSet. 2. Set flag to dump addr_hit_map when generating record info. @@ -816,6 +817,7 @@ class RecordData(object): with ThreadPoolExecutor(jobs) as executor: futures: List[Future] = [] + all_tasks = [] for lib_id, functions in lib_functions.items(): lib = self.libs.get_lib(lib_id) if not filter_lib(lib.name): @@ -823,33 +825,45 @@ class RecordData(object): dso_info = objdump.get_dso_info(lib.name, lib.build_id) if not dso_info: continue - # If there are not many functions, it's faster to disassemble them one by one. - # Otherwise it's faster to disassemble the whole binary. - if len(functions) < jobs: - for function in functions: - futures.append(executor.submit(self._disassemble_function, objdump, - dso_info, function)) - else: - futures.append(executor.submit(self._disassemble_binary, objdump, dso_info, - functions)) - for future in futures: - future.result() + tasks = self.split_disassembly_jobs(functions, disassemble_job_size) + logging.debug('create %d jobs to disassemble %d functions in %s', + len(tasks), len(functions), lib.name) + for task in tasks: + futures.append(executor.submit( + self._disassemble_functions, objdump, dso_info, task)) + all_tasks.append(task) + + for task, future in zip(all_tasks, futures): + result = future.result() + if result and len(result) == len(task): + for function, disassembly in zip(task, result): + function.disassembly = disassembly.lines + + logging.debug('finished all disassemble jobs') self.gen_addr_hit_map_in_record_info = True - def _disassemble_function(self, objdump: Objdump, dso_info, function: Function): - result = objdump.disassemble_function(dso_info, AddrRange(function.start_addr, - function.addr_len)) - if result: - function.disassembly = result.lines - - def _disassemble_binary(self, objdump: Objdump, dso_info, functions: List[Function]): + def split_disassembly_jobs(self, functions: List[Function], + disassemble_job_size: int) -> List[List[Function]]: + """ Decide how to split the task of dissassembly functions in one library. """ + if not functions: + return [] functions.sort(key=lambda f: f.start_addr) + result = [] + job_start_addr = None + for function in functions: + if (job_start_addr is None or + function.start_addr - job_start_addr > disassemble_job_size): + job_start_addr = function.start_addr + result.append([function]) + else: + result[-1].append(function) + return result + + def _disassemble_functions(self, objdump: Objdump, dso_info, + functions: List[Function]) -> Optional[List[Disassembly]]: addr_ranges = [AddrRange(f.start_addr, f.addr_len) for f in functions] - result = objdump.disassemble_functions(dso_info, addr_ranges) - if result: - for i in range(len(functions)): - functions[i].disassembly = result[i].lines + return objdump.disassemble_functions(dso_info, addr_ranges) def gen_record_info(self) -> Dict[str, Any]: """ Return json data which will be used by report_html.js. """ @@ -1010,6 +1024,8 @@ def get_args() -> argparse.Namespace: parser.add_argument('--add_source_code', action='store_true', help='Add source code.') parser.add_argument('--source_dirs', nargs='+', help='Source code directories.') parser.add_argument('--add_disassembly', action='store_true', help='Add disassembled code.') + parser.add_argument('--disassemble-job-size', type=int, default=1024*1024, + help='address range for one disassemble job') parser.add_argument('--binary_filter', nargs='+', help="""Annotate source code and disassembly only for selected binaries.""") parser.add_argument( @@ -1064,7 +1080,7 @@ def main(): if args.add_source_code: record_data.add_source_code(args.source_dirs, filter_lib, args.jobs) if args.add_disassembly: - record_data.add_disassembly(filter_lib, args.jobs) + record_data.add_disassembly(filter_lib, args.jobs, args.disassemble_job_size) # 3. Generate report html. report_generator = ReportGenerator(args.report_path) diff --git a/simpleperf/scripts/simpleperf_utils.py b/simpleperf/scripts/simpleperf_utils.py index 04939a8d..af4fca1b 100644 --- a/simpleperf/scripts/simpleperf_utils.py +++ b/simpleperf/scripts/simpleperf_utils.py @@ -869,6 +869,8 @@ class Objdump(object): """ Disassemble code for multiple addr ranges in a binary. sorted_addr_ranges should be sorted by addr_range.start. """ + if not sorted_addr_ranges: + return [] real_path, arch = dso_info objdump_path = self.objdump_paths.get(arch) if not objdump_path: @@ -878,7 +880,12 @@ class Objdump(object): self.objdump_paths[arch] = objdump_path # Run objdump. - args = [objdump_path, '-dlC', '--no-show-raw-insn', real_path] + start_addr = sorted_addr_ranges[0].start + stop_addr = max(addr_range.end for addr_range in sorted_addr_ranges) + args = [objdump_path, '-dlC', '--no-show-raw-insn', + '--start-address=0x%x' % start_addr, + '--stop-address=0x%x' % stop_addr, + real_path] if arch == 'arm' and 'llvm-objdump' in objdump_path: args += ['--print-imm-hex'] try: -- cgit v1.2.3 From 38fabf944b0c36349d2a456e09e4e6a790ceb0ae Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 5 Feb 2024 17:26:14 -0800 Subject: simpleperf: report_html.py: show 100 rows in sampleTable by default Bug: 322228003 Test: run report_html.py manually Change-Id: I38829ec82c647c4d524a0c996cc31eabef39ff54 --- simpleperf/scripts/report_html.js | 1 + 1 file changed, 1 insertion(+) diff --git a/simpleperf/scripts/report_html.js b/simpleperf/scripts/report_html.js index 8203dd36..17f780fe 100644 --- a/simpleperf/scripts/report_html.js +++ b/simpleperf/scripts/report_html.js @@ -701,6 +701,7 @@ class SampleTableView { let table = this.tableDiv.find('table'); let dataTable = table.DataTable({ lengthMenu: [10, 20, 50, 100, -1], + pageLength: 100, order: [0, 'desc'], data: data, responsive: true, -- cgit v1.2.3 From 52abb84e33123157347e3b05172f8372f4c4a09d Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 6 Feb 2024 12:21:54 -0800 Subject: simpleperf: report_html.py: disable sorting in ascending order for Total, Self, Samples It's unlikely that users want to sort these columns in ascending order. After disabling ascending order, when a user clicks one of these columns, the column is always sorted in descending order. Also sort these columns in descending order by default. Bug: 322228090 Test: run report_html.py Change-Id: I7b8a7a36f107ef6d935e71cbbd713426d4b1bec8 --- simpleperf/scripts/report_html.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/simpleperf/scripts/report_html.js b/simpleperf/scripts/report_html.js index 17f780fe..94e8ae5c 100644 --- a/simpleperf/scripts/report_html.js +++ b/simpleperf/scripts/report_html.js @@ -702,9 +702,12 @@ class SampleTableView { let dataTable = table.DataTable({ lengthMenu: [10, 20, 50, 100, -1], pageLength: 100, - order: [0, 'desc'], + order: [[0, 'desc'], [1, 'desc'], [2, 'desc']], data: data, responsive: true, + columnDefs: [ + { orderSequence: [ 'desc' ], targets: [0, 1, 2] }, + ], }); dataTable.column(7).visible(false); -- cgit v1.2.3