diff options
72 files changed, 857 insertions, 490 deletions
diff --git a/crypto-perf/Android.bp b/crypto-perf/Android.bp deleted file mode 100644 index 8f575ca9..00000000 --- a/crypto-perf/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ -package { - default_applicable_licenses: ["system_extras_crypto-perf_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "system_extras_crypto-perf_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - -cc_binary { - name: "crypto", - - cflags: [ - "-O0", - "-march=armv8-a+crypto", - "-Wall", - "-Werror", - ], - srcs: ["crypto.cpp"], - - enabled: false, - arch: { - arm64: { - enabled: true, - }, - }, -} diff --git a/crypto-perf/NOTICE b/crypto-perf/NOTICE deleted file mode 100644 index c77f135e..00000000 --- a/crypto-perf/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2012, 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/crypto-perf/crypto.cpp b/crypto-perf/crypto.cpp deleted file mode 100644 index a7228324..00000000 --- a/crypto-perf/crypto.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include <ctype.h> -#include <sched.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <unistd.h> -#define USEC_PER_SEC 1000000ULL -#define MAX_COUNT 1000000000ULL -#define NUM_INSTS_GARBAGE 18 - -// Contains information about benchmark options. -typedef struct { - int cpu_to_lock; - int locked_freq; -} command_data_t; - -void usage() { - printf("--------------------------------------------------------------------------------\n"); - printf("Usage:"); - printf(" crypto [--cpu_to_lock CPU] [--locked_freq FREQ_IN_KHZ]\n\n"); - printf("!!!!!!Lock the desired core to a desired frequency before invoking this benchmark.\n"); - printf("Hint: Set scaling_max_freq=scaling_min_freq=FREQ_IN_KHZ. FREQ_IN_KHZ " - "can be obtained from scaling_available_freq\n"); - printf("--------------------------------------------------------------------------------\n"); -} - -int processOptions(int argc, char** argv, command_data_t* cmd_data) { - // Initialize the command_flags. - cmd_data->cpu_to_lock = 0; - cmd_data->locked_freq = 1; - for (int i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - int* save_value = NULL; - if (strcmp(argv[i], "--cpu_to_lock") == 0) { - save_value = &cmd_data->cpu_to_lock; - } else if (strcmp(argv[i], "--locked_freq") == 0) { - save_value = &cmd_data->locked_freq; - } else { - printf("Unknown option %s\n", argv[i]); - return -1; - } - if (save_value) { - // Checking both characters without a strlen() call should be - // safe since as long as the argument exists, one character will - // be present (\0). And if the first character is '-', then - // there will always be a second character (\0 again). - if (i == argc - 1 || (argv[i + 1][0] == '-' && !isdigit(argv[i + 1][1]))) { - printf("The option %s requires one argument.\n", argv[i]); - return -1; - } - *save_value = (int)strtol(argv[++i], NULL, 0); - } - } - } - return 0; -} -/* Performs encryption on garbage values. In Cortex-A57 r0p1 and later - * revisions, pairs of dependent AESE/AESMC and AESD/AESIMC instructions are - * higher performance when adjacent, and in the described order below. */ -void garbage_encrypt() { - __asm__ __volatile__( - "aese v0.16b, v4.16b ;" - "aesmc v0.16b, v0.16b ;" - "aese v1.16b, v4.16b ;" - "aesmc v1.16b, v1.16b ;" - "aese v2.16b, v4.16b ;" - "aesmc v2.16b, v2.16b ;" - "aese v0.16b, v5.16b ;" - "aesmc v0.16b, v0.16b ;" - "aese v1.16b, v5.16b ;" - "aesmc v1.16b, v1.16b ;" - "aese v2.16b, v5.16b ;" - "aesmc v2.16b, v2.16b ;" - "aese v0.16b, v6.16b ;" - "aesmc v0.16b, v0.16b ;" - "aese v1.16b, v6.16b ;" - "aesmc v1.16b, v1.16b ;" - "aese v2.16b, v6.16b ;" - "aesmc v2.16b, v2.16b ;"); -} - -void garbage_decrypt() { - __asm__ __volatile__( - "aesd v0.16b, v4.16b ;" - "aesimc v0.16b, v0.16b ;" - "aesd v1.16b, v4.16b ;" - "aesimc v1.16b, v1.16b ;" - "aesd v2.16b, v4.16b ;" - "aesimc v2.16b, v2.16b ;" - "aesd v0.16b, v5.16b ;" - "aesimc v0.16b, v0.16b ;" - "aesd v1.16b, v5.16b ;" - "aesimc v1.16b, v1.16b ;" - "aesd v2.16b, v5.16b ;" - "aesimc v2.16b, v2.16b ;" - "aesd v0.16b, v6.16b ;" - "aesimc v0.16b, v0.16b ;" - "aesd v1.16b, v6.16b ;" - "aesimc v1.16b, v1.16b ;" - "aesd v2.16b, v6.16b ;" - "aesimc v2.16b, v2.16b ;"); -} - -int main(int argc, char** argv) { - usage(); - command_data_t cmd_data; - - if (processOptions(argc, argv, &cmd_data) == -1) { - usage(); - return -1; - } - unsigned long long count = 0; - struct timeval begin_time, end_time, elapsed_time; - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(cmd_data.cpu_to_lock, &cpuset); - if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { - perror("sched_setaffinity failed"); - return 1; - } - gettimeofday(&begin_time, NULL); - while (count < MAX_COUNT) { - garbage_encrypt(); - count++; - } - gettimeofday(&end_time, NULL); - timersub(&end_time, &begin_time, &elapsed_time); - fprintf(stderr, "encrypt: %llu us\n", - elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec); - fprintf(stderr, "encrypt instructions: %llu\n", MAX_COUNT * NUM_INSTS_GARBAGE); - fprintf(stderr, "encrypt instructions per second: %f\n", - (float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) / - (elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec)); - if (cmd_data.locked_freq != 0) { - fprintf(stderr, "encrypt instructions per cycle: %f\n", - (float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) / - ((elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec) * 1000 * - cmd_data.locked_freq)); - } - printf("--------------------------------------------------------------------------------\n"); - - count = 0; - gettimeofday(&begin_time, NULL); - while (count < MAX_COUNT) { - garbage_decrypt(); - count++; - } - gettimeofday(&end_time, NULL); - timersub(&end_time, &begin_time, &elapsed_time); - fprintf(stderr, "decrypt: %llu us\n", - elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec); - fprintf(stderr, "decrypt instructions: %llu\n", MAX_COUNT * NUM_INSTS_GARBAGE); - fprintf(stderr, "decrypt instructions per second: %f\n", - (float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) / - (elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec)); - if (cmd_data.locked_freq != 0) { - fprintf(stderr, "decrypt instructions per cycle: %f\n", - (float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) / - ((elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec) * 1000 * - cmd_data.locked_freq)); - } - return 0; -} diff --git a/partition_tools/dynamic_partitions_device_info.proto b/partition_tools/dynamic_partitions_device_info.proto index 8800dac7..82d20098 100644 --- a/partition_tools/dynamic_partitions_device_info.proto +++ b/partition_tools/dynamic_partitions_device_info.proto @@ -20,7 +20,7 @@ package android; // Keep in sync with proto files on EDI backend. Otherwise, new fields will // go ignored. -// Next: 6 +// Next: 7 message DynamicPartitionsDeviceInfoProto { bool enabled = 1; bool retrofit = 2; @@ -57,4 +57,14 @@ message DynamicPartitionsDeviceInfoProto { uint64 alignment_offset = 5 [json_name = "alignment_offset"]; } repeated BlockDevice block_devices = 5 [json_name = "block_devices"]; + + // Next: 4 + message SuperDevice { + string name = 1; + /** Used space in bytes */ + uint64 used_size = 2 [json_name = "used_size"]; + /** Total size of the super in bytes */ + uint64 total_size = 3 [json_name = "total_size"]; + } + SuperDevice super_device = 6 [json_name = "super_device"]; } diff --git a/partition_tools/lpdump.cc b/partition_tools/lpdump.cc index 97682940..4c1fe954 100644 --- a/partition_tools/lpdump.cc +++ b/partition_tools/lpdump.cc @@ -173,6 +173,12 @@ static bool MergeMetadata(const LpMetadata* metadata, block_device_proto->set_alignment(info.alignment); block_device_proto->set_alignment_offset(info.alignment_offset); } + + auto super_device_proto = proto->mutable_super_device(); + super_device_proto->set_name(GetSuperPartitionName()); + super_device_proto->set_used_size(builder->UsedSpace()); + super_device_proto->set_total_size(GetTotalSuperPartitionSize(*metadata)); + return true; } diff --git a/profcollectd/libprofcollectd/config.rs b/profcollectd/libprofcollectd/config.rs index 87242489..cbc5b7cd 100644 --- a/profcollectd/libprofcollectd/config.rs +++ b/profcollectd/libprofcollectd/config.rs @@ -27,7 +27,7 @@ use std::path::Path; use std::str::FromStr; use std::time::Duration; -const PROFCOLLECT_CONFIG_NAMESPACE: &str = "profcollect_native_boot"; +const PROFCOLLECT_CONFIG_NAMESPACE: &str = "aconfig_flags.profcollect_native_boot"; const PROFCOLLECT_NODE_ID_PROPERTY: &str = "persist.profcollectd.node_id"; const DEFAULT_BINARY_FILTER: &str = @@ -57,8 +57,6 @@ pub struct Config { pub build_fingerprint: String, /// Interval between collections. pub collection_interval: Duration, - /// Length of time each collection lasts for. - pub sampling_period: Duration, /// An optional filter to limit which binaries to or not to profile. pub binary_filter: String, /// Maximum size of the trace directory. @@ -75,7 +73,6 @@ impl Config { "collection_interval", 600, )?), - sampling_period: Duration::from_millis(get_device_config("sampling_period", 500)?), binary_filter: get_device_config("binary_filter", DEFAULT_BINARY_FILTER.to_string())?, max_trace_limit: get_device_config( "max_trace_limit", @@ -85,9 +82,9 @@ impl Config { } } -impl ToString for Config { - fn to_string(&self) -> String { - serde_json::to_string(self).expect("Failed to deserialise configuration.") +impl std::fmt::Display for Config { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", serde_json::to_string(self).expect("Failed to deserialise configuration.")) } } @@ -123,6 +120,13 @@ where Ok(T::from_str(&config)?) } +pub fn get_sampling_period() -> Duration { + let default_period = 500; + Duration::from_millis( + get_device_config("sampling_period", default_period).unwrap_or(default_period), + ) +} + fn get_property<T>(key: &str, default_value: T) -> Result<T> where T: FromStr + ToString, @@ -162,3 +166,11 @@ pub fn clear_data() -> Result<()> { remove_files(&REPORT_OUTPUT_DIR)?; Ok(()) } +pub fn clear_processed_files() -> Result<()> { + read_dir(&PROFILE_OUTPUT_DIR as &Path)? + .filter_map(|e| e.ok()) + .map(|e| e.path()) + .filter(|e| e.is_file() && e != (&CONFIG_FILE as &Path)) + .try_for_each(remove_file)?; + Ok(()) +} diff --git a/profcollectd/libprofcollectd/report.rs b/profcollectd/libprofcollectd/report.rs index e0f2ec84..60410c1a 100644 --- a/profcollectd/libprofcollectd/report.rs +++ b/profcollectd/libprofcollectd/report.rs @@ -29,7 +29,7 @@ use zip::write::FileOptions; use zip::CompressionMethod::Deflated; use zip::ZipWriter; -use crate::config::Config; +use crate::config::{clear_processed_files, Config}; pub const NO_USAGE_SETTING: i32 = -1; @@ -80,6 +80,7 @@ pub fn pack_report( zip.write_all(usage_setting.to_string().as_bytes())?; } zip.finish()?; + clear_processed_files()?; Ok(report_filename) } diff --git a/profcollectd/libprofcollectd/scheduler.rs b/profcollectd/libprofcollectd/scheduler.rs index 5558f581..8695f57c 100644 --- a/profcollectd/libprofcollectd/scheduler.rs +++ b/profcollectd/libprofcollectd/scheduler.rs @@ -25,7 +25,7 @@ use std::sync::Mutex; use std::thread; use std::time::{Duration, Instant}; -use crate::config::{Config, LOG_FILE, PROFILE_OUTPUT_DIR, TRACE_OUTPUT_DIR}; +use crate::config::{get_sampling_period, Config, LOG_FILE, PROFILE_OUTPUT_DIR, TRACE_OUTPUT_DIR}; use crate::trace_provider::{self, TraceProvider}; use anyhow::{anyhow, ensure, Context, Result}; @@ -73,7 +73,7 @@ impl Scheduler { trace_provider.lock().unwrap().trace( &TRACE_OUTPUT_DIR, "periodic", - &config.sampling_period, + &get_sampling_period(), &config.binary_filter, ); } @@ -100,7 +100,7 @@ impl Scheduler { trace_provider.lock().unwrap().trace( &TRACE_OUTPUT_DIR, tag, - &config.sampling_period, + &get_sampling_period(), &config.binary_filter, ); } diff --git a/simpleperf/BranchListFile_test.cpp b/simpleperf/BranchListFile_test.cpp index 759b0218..d7f9a6a5 100644 --- a/simpleperf/BranchListFile_test.cpp +++ b/simpleperf/BranchListFile_test.cpp @@ -20,6 +20,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(BranchListFile, etm_branch_to_proto_string) { std::vector<bool> branch; for (size_t i = 0; i < 100; i++) { diff --git a/simpleperf/CallChainJoiner_test.cpp b/simpleperf/CallChainJoiner_test.cpp index 90460e03..4b0fa29f 100644 --- a/simpleperf/CallChainJoiner_test.cpp +++ b/simpleperf/CallChainJoiner_test.cpp @@ -33,6 +33,7 @@ static bool JoinCallChain(LRUCache& cache, uint32_t tid, const std::vector<uint6 return tmp_ip == expected_output_ip && tmp_sp == expected_output_sp; } +// @CddTest = 6.1/C-0-2 TEST(LRUCache, different_nodes) { LRUCache cache(sizeof(CacheNode) * 2, 1); ASSERT_EQ(cache.Stat().max_node_count, 2u); @@ -65,6 +66,7 @@ TEST(LRUCache, different_nodes) { ASSERT_NE(cache.FindNode(1, ip[0], sp2[0]), nullptr); } +// @CddTest = 6.1/C-0-2 TEST(LRUCache, extend_chains) { // matched_node_count_to_extend_callchain = 1 // c -> b @@ -93,6 +95,7 @@ TEST(LRUCache, extend_chains) { ASSERT_EQ(cache3.Stat().used_node_count, 4u); } +// @CddTest = 6.1/C-0-2 TEST(LRUCache, avoid_ip_sp_loop) { LRUCache cache(sizeof(CacheNode) * 2, 1); std::vector<uint64_t> ip = {0xa, 0xb}; @@ -104,6 +107,7 @@ TEST(LRUCache, avoid_ip_sp_loop) { ASSERT_EQ(cache.Stat().recycled_node_count, 0u); } +// @CddTest = 6.1/C-0-2 TEST(LRUCache, one_chain) { LRUCache cache(sizeof(CacheNode) * 4, 1); ASSERT_EQ(cache.Stat().max_node_count, 4u); @@ -125,6 +129,7 @@ TEST(LRUCache, one_chain) { ASSERT_EQ(cache.Stat().recycled_node_count, 0u); } +// @CddTest = 6.1/C-0-2 TEST(LRUCache, many_chains) { LRUCache cache(sizeof(CacheNode) * 12, 1); // 4 -> 3 -> 2 -> 1 @@ -149,6 +154,7 @@ TEST(LRUCache, many_chains) { ASSERT_EQ(cache.FindNode(0, 0xa, 0xa), nullptr); } +// @CddTest = 6.1/C-0-2 class CallChainJoinerTest : public ::testing::Test { protected: void SetUp() override { @@ -164,6 +170,7 @@ class CallChainJoinerTest : public ::testing::Test { std::unique_ptr<ScopedTempFiles> scoped_temp_files_; }; +// @CddTest = 6.1/C-0-2 TEST_F(CallChainJoinerTest, smoke) { CallChainJoiner joiner(sizeof(CacheNode) * 1024, 1, true); for (pid_t pid = 0; pid < 10; ++pid) { @@ -230,6 +237,7 @@ TEST_F(CallChainJoinerTest, smoke) { ASSERT_EQ(joiner.GetStat().after_join_max_chain_length, 5u); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainJoinerTest, no_original_chains) { CallChainJoiner joiner(sizeof(CacheNode) * 1024, 1, false); ASSERT_TRUE(joiner.AddCallChain(0, 0, CallChainJoiner::ORIGINAL_OFFLINE, {1}, {1})); @@ -249,6 +257,7 @@ TEST_F(CallChainJoinerTest, no_original_chains) { joiner.DumpStat(); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainJoinerTest, no_chains) { CallChainJoiner joiner(sizeof(CacheNode) * 1024, 1, false); ASSERT_TRUE(joiner.JoinCallChains()); diff --git a/simpleperf/ETMConstants.h b/simpleperf/ETMConstants.h index 78e9cc26..003d88ab 100644 --- a/simpleperf/ETMConstants.h +++ b/simpleperf/ETMConstants.h @@ -24,6 +24,7 @@ static constexpr int ETM_OPT_CTXTID = 14; static constexpr int ETM_OPT_CTXTID2 = 15; static constexpr int ETM_OPT_TS = 28; // For etm_config_reg: +static constexpr int ETM4_CFG_BIT_CCI = 4; static constexpr int ETM4_CFG_BIT_CTXTID = 6; static constexpr int ETM4_CFG_BIT_VMID = 7; static constexpr int ETM4_CFG_BIT_TS = 11; diff --git a/simpleperf/ETMRecorder.cpp b/simpleperf/ETMRecorder.cpp index cde1eb2b..a722df5c 100644 --- a/simpleperf/ETMRecorder.cpp +++ b/simpleperf/ETMRecorder.cpp @@ -229,6 +229,7 @@ void ETMRecorder::BuildEtmConfig() { } if (cycles_supported) { etm_event_config_ |= 1ULL << ETM_OPT_CYCACC; + etm_config_reg_ |= 1U << ETM4_CFG_BIT_CCI; if (cycle_threshold_) { cc_threshold_config_ |= cycle_threshold_; diff --git a/simpleperf/IOEventLoop_test.cpp b/simpleperf/IOEventLoop_test.cpp index 74a45281..fbbc0fc8 100644 --- a/simpleperf/IOEventLoop_test.cpp +++ b/simpleperf/IOEventLoop_test.cpp @@ -26,6 +26,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, read) { int fd[2]; ASSERT_EQ(0, pipe(fd)); @@ -65,6 +66,7 @@ TEST(IOEventLoop, read) { close(fd[1]); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, write) { int fd[2]; ASSERT_EQ(0, pipe(fd)); @@ -101,6 +103,7 @@ TEST(IOEventLoop, write) { ASSERT_EQ(100, count); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, signal) { IOEventLoop loop; int count = 0; @@ -147,10 +150,12 @@ void TestPeriodicEvents(int period_in_us, int iterations) { ASSERT_LT(time_used, max_time_in_sec); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, periodic) { TestPeriodicEvents(1000, 100); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, one_time_event) { int duration_in_us = 1000; timeval tv = {}; @@ -179,6 +184,7 @@ TEST(IOEventLoop, one_time_event) { ASSERT_LT(time_used, max_time_in_sec); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, read_and_del_event) { int fd[2]; ASSERT_EQ(0, pipe(fd)); @@ -204,6 +210,7 @@ TEST(IOEventLoop, read_and_del_event) { close(fd[1]); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, disable_enable_event) { int fd[2]; ASSERT_EQ(0, pipe(fd)); @@ -241,6 +248,7 @@ TEST(IOEventLoop, disable_enable_event) { close(fd[1]); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, disable_enable_periodic_event) { timeval tv; tv.tv_sec = 0; @@ -267,11 +275,13 @@ TEST(IOEventLoop, disable_enable_periodic_event) { ASSERT_EQ(2u, periodic_count); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, exit_before_loop) { IOEventLoop loop; ASSERT_TRUE(loop.ExitLoop()); } +// @CddTest = 6.1/C-0-2 TEST(IOEventLoop, priority) { int low_priority_fd[2]; ASSERT_EQ(0, pipe(low_priority_fd)); diff --git a/simpleperf/JITDebugReader_test.cpp b/simpleperf/JITDebugReader_test.cpp index d46236a6..4aaad813 100644 --- a/simpleperf/JITDebugReader_test.cpp +++ b/simpleperf/JITDebugReader_test.cpp @@ -32,6 +32,7 @@ using namespace simpleperf; using namespace simpleperf::JITDebugReader_impl; +// @CddTest = 6.1/C-0-2 TEST(TempSymFile, smoke) { TemporaryFile tmpfile; std::unique_ptr<TempSymFile> symfile = TempSymFile::Create(tmpfile.path, false); @@ -51,6 +52,7 @@ TEST(TempSymFile, smoke) { ASSERT_EQ(strncmp(test_data.c_str(), buf, test_data.size()), 0); } +// @CddTest = 6.1/C-0-2 TEST(JITDebugReader, read_dex_file_in_memory) { // 1. Create dex file in memory. Use mmap instead of malloc, to avoid the pointer from // being modified by memory tag (or pointer authentication?) on ARM64. diff --git a/simpleperf/MapRecordReader_test.cpp b/simpleperf/MapRecordReader_test.cpp index 669b31fb..d194cb56 100644 --- a/simpleperf/MapRecordReader_test.cpp +++ b/simpleperf/MapRecordReader_test.cpp @@ -26,6 +26,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 class MapRecordReaderTest : public ::testing::Test { protected: bool CreateMapRecordReader() { @@ -54,12 +55,14 @@ class MapRecordReaderTest : public ::testing::Test { size_t comm_record_count_ = 0; }; +// @CddTest = 6.1/C-0-2 TEST_F(MapRecordReaderTest, ReadKernelMaps) { ASSERT_TRUE(CreateMapRecordReader()); ASSERT_TRUE(reader_->ReadKernelMaps()); ASSERT_GT(map_record_count_, 0); } +// @CddTest = 6.1/C-0-2 TEST_F(MapRecordReaderTest, ReadProcessMaps) { ASSERT_TRUE(CreateMapRecordReader()); ASSERT_TRUE(reader_->ReadProcessMaps(getpid(), 0)); @@ -67,6 +70,7 @@ TEST_F(MapRecordReaderTest, ReadProcessMaps) { ASSERT_GT(comm_record_count_, 0); } +// @CddTest = 6.1/C-0-2 TEST_F(MapRecordReaderTest, MapRecordThread) { #ifdef __ANDROID__ std::string tmpdir = "/data/local/tmp"; diff --git a/simpleperf/OfflineUnwinder_test.cpp b/simpleperf/OfflineUnwinder_test.cpp index 7f9d9aab..c13e229c 100644 --- a/simpleperf/OfflineUnwinder_test.cpp +++ b/simpleperf/OfflineUnwinder_test.cpp @@ -44,6 +44,7 @@ bool CheckUnwindMaps(UnwindMaps& maps, const MapSet& map_set) { return true; } +// @CddTest = 6.1/C-0-2 TEST(OfflineUnwinder, UnwindMaps) { // 1. Create fake map entries. std::unique_ptr<Dso> fake_dso = Dso::CreateDso(DSO_UNKNOWN_FILE, "unknown"); @@ -90,6 +91,7 @@ TEST(OfflineUnwinder, UnwindMaps) { ASSERT_TRUE(CheckUnwindMaps(maps, map_set)); } +// @CddTest = 6.1/C-0-2 TEST(OfflineUnwinder, CollectMetaInfo) { std::unordered_map<std::string, std::string> info_map; OfflineUnwinder::CollectMetaInfo(&info_map); @@ -100,6 +102,7 @@ TEST(OfflineUnwinder, CollectMetaInfo) { } } +// @CddTest = 6.1/C-0-2 TEST(OfflineUnwinder, ARM64PackMask) { std::unordered_map<std::string, std::string> info_map; info_map[OfflineUnwinder::META_KEY_ARM64_PAC_MASK] = "0xff00000000"; diff --git a/simpleperf/ProbeEvents_test.cpp b/simpleperf/ProbeEvents_test.cpp index be135386..630c5761 100644 --- a/simpleperf/ProbeEvents_test.cpp +++ b/simpleperf/ProbeEvents_test.cpp @@ -23,6 +23,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(probe_events, ParseKprobeEventName) { ProbeEvent event; ASSERT_TRUE(ProbeEvents::ParseKprobeEventName("p:myprobe do_sys_open", &event)); diff --git a/simpleperf/RecordFilter_test.cpp b/simpleperf/RecordFilter_test.cpp index 3bbda4c8..1d5d1a06 100644 --- a/simpleperf/RecordFilter_test.cpp +++ b/simpleperf/RecordFilter_test.cpp @@ -30,6 +30,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 class RecordFilterTest : public ::testing::Test { public: RecordFilterTest() : filter(thread_tree) {} @@ -58,10 +59,12 @@ class RecordFilterTest : public ::testing::Test { std::unique_ptr<SampleRecord> record; }; +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, no_filter) { ASSERT_TRUE(filter.Check(GetRecord(0, 0))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, cpu) { filter.AddCpus({1}); SampleRecord& r = GetRecord(0, 0); @@ -71,18 +74,21 @@ TEST_F(RecordFilterTest, cpu) { ASSERT_FALSE(filter.Check(r)); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, exclude_pid) { filter.AddPids({1}, true); ASSERT_FALSE(filter.Check(GetRecord(1, 1))); ASSERT_TRUE(filter.Check(GetRecord(2, 2))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, exclude_tid) { filter.AddTids({1}, true); ASSERT_FALSE(filter.Check(GetRecord(1, 1))); ASSERT_TRUE(filter.Check(GetRecord(1, 2))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, exclude_process_name_regex) { ASSERT_TRUE(filter.AddProcessNameRegex("processA", true)); thread_tree.SetThreadName(1, 1, "processA1"); @@ -91,6 +97,7 @@ TEST_F(RecordFilterTest, exclude_process_name_regex) { ASSERT_TRUE(filter.Check(GetRecord(2, 2))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, exclude_thread_name_regex) { ASSERT_TRUE(filter.AddThreadNameRegex("threadA", true)); thread_tree.SetThreadName(1, 1, "processA_threadA"); @@ -100,6 +107,7 @@ TEST_F(RecordFilterTest, exclude_thread_name_regex) { } #if defined(__linux__) +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, exclude_uid) { pid_t pid = getpid(); std::optional<uint32_t> uid = GetProcessUid(pid); @@ -112,18 +120,21 @@ TEST_F(RecordFilterTest, exclude_uid) { } #endif // defined(__linux__) +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, include_pid) { filter.AddPids({1}, false); ASSERT_TRUE(filter.Check(GetRecord(1, 1))); ASSERT_FALSE(filter.Check(GetRecord(2, 2))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, include_tid) { filter.AddTids({1}, false); ASSERT_TRUE(filter.Check(GetRecord(1, 1))); ASSERT_FALSE(filter.Check(GetRecord(1, 2))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, include_process_name_regex) { ASSERT_TRUE(filter.AddProcessNameRegex("processA", false)); thread_tree.SetThreadName(1, 1, "processA1"); @@ -132,6 +143,7 @@ TEST_F(RecordFilterTest, include_process_name_regex) { ASSERT_FALSE(filter.Check(GetRecord(2, 2))); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, include_thread_name_regex) { ASSERT_TRUE(filter.AddThreadNameRegex("threadA", false)); thread_tree.SetThreadName(1, 1, "processA_threadA"); @@ -141,6 +153,7 @@ TEST_F(RecordFilterTest, include_thread_name_regex) { } #if defined(__linux__) +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, include_uid) { pid_t pid = getpid(); std::optional<uint32_t> uid = GetProcessUid(pid); @@ -152,6 +165,7 @@ TEST_F(RecordFilterTest, include_uid) { } #endif // defined(__linux__) +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, global_time_filter) { ASSERT_TRUE( SetFilterData("GLOBAL_BEGIN 1000\n" @@ -179,6 +193,7 @@ TEST_F(RecordFilterTest, global_time_filter) { ASSERT_FALSE(filter.Check(r)); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, process_time_filter) { ASSERT_TRUE( SetFilterData("PROCESS_BEGIN 1 1000\n" @@ -202,6 +217,7 @@ TEST_F(RecordFilterTest, process_time_filter) { ASSERT_FALSE(filter.Check(r)); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, thread_time_filter) { ASSERT_TRUE( SetFilterData("THREAD_BEGIN 1 1000\n" @@ -225,6 +241,7 @@ TEST_F(RecordFilterTest, thread_time_filter) { ASSERT_FALSE(filter.Check(r)); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, clock_in_time_filter) { // If there is no filter data, any clock is fine. ASSERT_TRUE(filter.CheckClock("monotonic")); @@ -239,6 +256,7 @@ TEST_F(RecordFilterTest, clock_in_time_filter) { ASSERT_FALSE(filter.CheckClock("monotonic")); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, error_in_time_filter) { // no timestamp error ASSERT_FALSE(SetFilterData("GLOBAL_BEGIN")); @@ -290,6 +308,7 @@ class ParseRecordFilterCommand : public Command { } // namespace +// @CddTest = 6.1/C-0-2 TEST_F(RecordFilterTest, parse_options) { ParseRecordFilterCommand filter_cmd(filter); diff --git a/simpleperf/RecordReadThread.cpp b/simpleperf/RecordReadThread.cpp index 2ab61278..2d034bc1 100644 --- a/simpleperf/RecordReadThread.cpp +++ b/simpleperf/RecordReadThread.cpp @@ -408,6 +408,7 @@ bool RecordReadThread::HandleAddEventFds(IOEventLoop& loop, success = false; break; } + has_etm_events_ = true; } cpu_map[fd->Cpu()] = fd; } else { @@ -620,6 +621,9 @@ void RecordReadThread::PushRecordToRecordBuffer(KernelRecordReader* kernel_recor } void RecordReadThread::ReadAuxDataFromKernelBuffer(bool* has_data) { + if (!has_etm_events_) { + return; + } for (auto& reader : kernel_record_readers_) { EventFd* event_fd = reader.GetEventFd(); if (event_fd->HasAuxBuffer()) { @@ -659,6 +663,14 @@ void RecordReadThread::ReadAuxDataFromKernelBuffer(bool* has_data) { } bool RecordReadThread::SendDataNotificationToMainThread() { + if (has_etm_events_) { + // For ETM recording, the default buffer size is large enough to hold ETM data for several + // seconds. To reduce impact of processing ETM data (especially when --decode-etm is used), + // delay processing ETM data until the buffer is half full. + if (record_buffer_.GetFreeSize() >= record_buffer_.size() / 2) { + return true; + } + } if (!has_data_notification_.load(std::memory_order_relaxed)) { has_data_notification_ = true; char unused = 0; diff --git a/simpleperf/RecordReadThread.h b/simpleperf/RecordReadThread.h index c104b083..893f8234 100644 --- a/simpleperf/RecordReadThread.h +++ b/simpleperf/RecordReadThread.h @@ -211,6 +211,7 @@ class RecordReadThread { std::unique_ptr<std::thread> read_thread_; std::vector<KernelRecordReader> kernel_record_readers_; pid_t exclude_pid_ = -1; + bool has_etm_events_ = false; std::unordered_set<EventFd*> event_fds_disabled_by_kernel_; diff --git a/simpleperf/RecordReadThread_test.cpp b/simpleperf/RecordReadThread_test.cpp index e597e443..2ff9a460 100644 --- a/simpleperf/RecordReadThread_test.cpp +++ b/simpleperf/RecordReadThread_test.cpp @@ -32,6 +32,7 @@ using ::testing::Truly; using namespace simpleperf; +// @CddTest = 6.1/C-0-2 class RecordBufferTest : public ::testing::Test { protected: void PushRecord(uint32_t type, size_t size) { @@ -57,6 +58,7 @@ class RecordBufferTest : public ::testing::Test { std::unique_ptr<RecordBuffer> buffer_; }; +// @CddTest = 6.1/C-0-2 TEST_F(RecordBufferTest, fifo) { for (size_t loop = 0; loop < 10; ++loop) { buffer_.reset(new RecordBuffer(sizeof(perf_event_header) * 10)); @@ -73,6 +75,7 @@ TEST_F(RecordBufferTest, fifo) { } } +// @CddTest = 6.1/C-0-2 TEST(RecordParser, smoke) { std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(GetTestData(PERF_DATA_NO_UNWIND)); @@ -113,6 +116,7 @@ TEST(RecordParser, smoke) { })); } +// @CddTest = 6.1/C-0-2 TEST(RecordParser, GetStackSizePos_with_PerfSampleReadType) { const EventType* type = FindEventTypeByName("cpu-clock"); ASSERT_TRUE(type != nullptr); @@ -190,6 +194,7 @@ static inline std::function<bool(size_t&)> SetArg(size_t value) { }; } +// @CddTest = 6.1/C-0-2 TEST(KernelRecordReader, smoke) { // 1. Create fake records. perf_event_attr attr = CreateFakeEventAttr(); @@ -229,6 +234,7 @@ TEST(KernelRecordReader, smoke) { ASSERT_FALSE(reader.MoveToNextRecord(parser)); } +// @CddTest = 6.1/C-0-2 class RecordReadThreadTest : public ::testing::Test { protected: std::vector<EventFd*> CreateFakeEventFds(const perf_event_attr& attr, size_t event_fd_count) { @@ -270,6 +276,7 @@ class RecordReadThreadTest : public ::testing::Test { std::vector<std::unique_ptr<MockEventFd>> event_fds_; }; +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, handle_cmds) { perf_event_attr attr = CreateFakeEventAttr(); records_ = CreateFakeRecords(attr, 2, 0, 0); @@ -291,6 +298,7 @@ TEST_F(RecordReadThreadTest, handle_cmds) { ASSERT_TRUE(thread.StopReadThread()); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, read_records) { perf_event_attr attr = CreateFakeEventAttr(); RecordReadThread thread(128 * 1024, attr, 1, 1, 0); @@ -323,6 +331,7 @@ TEST_F(RecordReadThreadTest, read_records) { } } +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, process_sample_record) { perf_event_attr attr = CreateFakeEventAttr(); attr.sample_type |= PERF_SAMPLE_STACK_USER; @@ -377,6 +386,7 @@ TEST_F(RecordReadThreadTest, process_sample_record) { // Test that the data notification exists until the RecordBuffer is empty. So we can read all // records even if reading one record at a time. +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, has_data_notification_until_buffer_empty) { perf_event_attr attr = CreateFakeEventAttr(); RecordReadThread thread(128 * 1024, attr, 1, 1, 0); @@ -403,6 +413,7 @@ TEST_F(RecordReadThreadTest, has_data_notification_until_buffer_empty) { ASSERT_TRUE(thread.RemoveEventFds(event_fds)); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, no_truncated_samples) { perf_event_attr attr = CreateFakeEventAttr(); attr.sample_type |= PERF_SAMPLE_STACK_USER; @@ -426,6 +437,7 @@ TEST_F(RecordReadThreadTest, no_truncated_samples) { ASSERT_EQ(thread.GetStat().userspace_truncated_stack_samples, 0u); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, exclude_perf) { perf_event_attr attr = CreateFakeEventAttr(); attr.sample_type |= PERF_SAMPLE_STACK_USER; @@ -475,6 +487,7 @@ struct FakeAuxData { : buf1(buf1_size, c), buf2(buf2_size, c), pad(pad_size, 0), lost(lost) {} }; +// @CddTest = 6.1/C-0-2 TEST_F(RecordReadThreadTest, read_aux_data) { ScopedEventTypes scoped_types("cs-etm,0,0"); const EventType* type = FindEventTypeByName("cs-etm"); @@ -569,4 +582,4 @@ TEST_F(RecordReadThreadTest, read_aux_data) { } ASSERT_EQ(aux_data_size, thread.GetStat().aux_data_size); ASSERT_EQ(lost_aux_data_size, thread.GetStat().lost_aux_data_size); -}
\ No newline at end of file +} diff --git a/simpleperf/RegEx_test.cpp b/simpleperf/RegEx_test.cpp index cbb25ae0..978772c4 100644 --- a/simpleperf/RegEx_test.cpp +++ b/simpleperf/RegEx_test.cpp @@ -20,6 +20,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(RegEx, smoke) { auto re = RegEx::Create("b+"); ASSERT_EQ(re->GetPattern(), "b+"); @@ -42,6 +43,7 @@ TEST(RegEx, smoke) { ASSERT_EQ(re->Replace("ababb", "c").value(), "acac"); } +// @CddTest = 6.1/C-0-2 TEST(RegEx, invalid_pattern) { ASSERT_TRUE(RegEx::Create("?hello") == nullptr); } diff --git a/simpleperf/cmd_api_test.cpp b/simpleperf/cmd_api_test.cpp index 41a2a165..d1fb044a 100644 --- a/simpleperf/cmd_api_test.cpp +++ b/simpleperf/cmd_api_test.cpp @@ -90,6 +90,7 @@ static void RecordApp(const std::string& package_name, const std::string& apk_pa #endif // defined(__ANDROID__) +// @CddTest = 6.1/C-0-2 TEST(cmd_api, java_app) { #if defined(__ANDROID__) RecordApp("simpleperf.demo.java_api", GetTestData("java_api.apk")); @@ -98,10 +99,11 @@ TEST(cmd_api, java_app) { #endif } +// @CddTest = 6.1/C-0-2 TEST(cmd_api, native_app) { #if defined(__ANDROID__) RecordApp("simpleperf.demo.cpp_api", GetTestData("cpp_api.apk")); #else GTEST_LOG_(INFO) << "This test tests recording apps on Android."; #endif -}
\ No newline at end of file +} diff --git a/simpleperf/cmd_boot_record_test.cpp b/simpleperf/cmd_boot_record_test.cpp index 25b2aff0..73266ce2 100644 --- a/simpleperf/cmd_boot_record_test.cpp +++ b/simpleperf/cmd_boot_record_test.cpp @@ -28,6 +28,7 @@ static std::unique_ptr<Command> BootRecordCmd() { return CreateCommandInstance("boot-record"); } +// @CddTest = 6.1/C-0-2 TEST(cmd_boot_record, smoke) { TEST_REQUIRE_ROOT(); ASSERT_TRUE(BootRecordCmd()->Run({"--enable", "-a -g --duration 1"})); diff --git a/simpleperf/cmd_debug_unwind_test.cpp b/simpleperf/cmd_debug_unwind_test.cpp index d0bbfe8f..2bf14a59 100644 --- a/simpleperf/cmd_debug_unwind_test.cpp +++ b/simpleperf/cmd_debug_unwind_test.cpp @@ -35,6 +35,7 @@ static std::unique_ptr<Command> DebugUnwindCmd() { return CreateCommandInstance("debug-unwind"); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, unwind_sample_option) { std::string input_data = GetTestData(PERF_DATA_NO_UNWIND); CaptureStdout capture; @@ -44,6 +45,7 @@ TEST(cmd_debug_unwind, unwind_sample_option) { ASSERT_NE(capture.Finish().find("sample_time: 1516379654300997"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, sample_time_option) { std::string input_data = GetTestData(PERF_DATA_NO_UNWIND); CaptureStdout capture; @@ -58,6 +60,7 @@ TEST(cmd_debug_unwind, sample_time_option) { ASSERT_NE(output.find("sample_time: 1516379655959122"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, output_option) { std::string input_data = GetTestData(PERF_DATA_NO_UNWIND); TemporaryFile tmpfile; @@ -69,6 +72,7 @@ TEST(cmd_debug_unwind, output_option) { ASSERT_NE(output.find("sample_time: 1516379654300997"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, symfs_option) { std::string input_data = GetTestData(NATIVELIB_IN_APK_PERF_DATA); CaptureStdout capture; @@ -80,6 +84,7 @@ TEST(cmd_debug_unwind, symfs_option) { std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, unwind_with_ip_zero_in_callchain) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); @@ -88,6 +93,7 @@ TEST(cmd_debug_unwind, unwind_with_ip_zero_in_callchain) { ASSERT_NE(capture.Finish().find("sample_time: 152526249937103"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, unwind_embedded_lib_in_apk) { // Check if we can unwind through a native library embedded in an apk. In the profiling data // file, there is a sample with ip address pointing to @@ -107,6 +113,7 @@ TEST(cmd_debug_unwind, unwind_embedded_lib_in_apk) { ASSERT_NE(output.find("dso_2: /bionic/lib64/libc.so"), std::string::npos) << output; } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, unwind_sample_in_unwinding_debug_info_file) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); @@ -116,6 +123,7 @@ TEST(cmd_debug_unwind, unwind_sample_in_unwinding_debug_info_file) { ASSERT_NE(output.find("symbol_5: android.os.Handler.post"), std::string::npos) << output; } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, skip_sample_print_option) { std::string input_data = GetTestData(PERF_DATA_NO_UNWIND); CaptureStdout capture; @@ -128,6 +136,7 @@ TEST(cmd_debug_unwind, skip_sample_print_option) { ASSERT_NE(output.find("unwinding_sample_count: 8"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, generate_test_file) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -143,6 +152,7 @@ TEST(cmd_debug_unwind, generate_test_file) { ASSERT_NE(output.find("symbol_2: android.os.Handler.enqueueMessage"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, generate_test_file_with_build_id) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -157,6 +167,7 @@ TEST(cmd_debug_unwind, generate_test_file_with_build_id) { ASSERT_STREQ(build_ids[0].filename, "/apex/com.android.runtime/lib64/bionic/libc.so"); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, generate_report) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -169,6 +180,7 @@ TEST(cmd_debug_unwind, generate_report) { ASSERT_NE(output.find("symbol_2: android.os.Handler.enqueueMessage"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_debug_unwind, unwind_sample_for_small_map_range) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); diff --git a/simpleperf/cmd_dumprecord_test.cpp b/simpleperf/cmd_dumprecord_test.cpp index e6cc5e23..ffdcbc2d 100644 --- a/simpleperf/cmd_dumprecord_test.cpp +++ b/simpleperf/cmd_dumprecord_test.cpp @@ -26,22 +26,27 @@ static std::unique_ptr<Command> DumpCmd() { return CreateCommandInstance("dump"); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, record_file_option) { ASSERT_TRUE(DumpCmd()->Run({GetTestData("perf.data")})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, input_option) { ASSERT_TRUE(DumpCmd()->Run({"-i", GetTestData("perf.data")})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, dump_data_generated_by_linux_perf) { ASSERT_TRUE(DumpCmd()->Run({GetTestData(PERF_DATA_GENERATED_BY_LINUX_PERF)})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, dump_callchain_records) { ASSERT_TRUE(DumpCmd()->Run({GetTestData(PERF_DATA_WITH_CALLCHAIN_RECORD)})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, dump_callchain_of_sample_records) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); @@ -51,6 +56,7 @@ TEST(cmd_dump, dump_callchain_of_sample_records) { ASSERT_NE(data.find("__ioctl (/system/lib64/libc.so[+70b6c])"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, dump_tracepoint_fields_of_sample_records) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); @@ -66,6 +72,7 @@ TEST(cmd_dump, dump_tracepoint_fields_of_sample_records) { std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, etm_data) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); @@ -78,6 +85,7 @@ TEST(cmd_dump, etm_data) { ASSERT_NE(data.find("OCSD_GEN_TRC_ELEM_INSTR_RANGE"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_dump, dump_arm_regs_recorded_in_arm64) { ASSERT_TRUE(DumpCmd()->Run({GetTestData("perf_with_arm_regs.data")})); } diff --git a/simpleperf/cmd_inject_test.cpp b/simpleperf/cmd_inject_test.cpp index 9bb1efba..b6e6456c 100644 --- a/simpleperf/cmd_inject_test.cpp +++ b/simpleperf/cmd_inject_test.cpp @@ -59,6 +59,7 @@ static void CheckMatchingExpectedData(const std::string& name, std::string& data ASSERT_EQ(data, expected_data); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, smoke) { std::string data; ASSERT_TRUE(RunInjectCmd({}, &data)); @@ -67,6 +68,7 @@ TEST(cmd_inject, smoke) { CheckMatchingExpectedData("perf_inject.data", data); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, binary_option) { // Test that data for etm_test_loop is generated when selected by --binary. std::string data; @@ -86,10 +88,12 @@ TEST(cmd_inject, binary_option) { ASSERT_EQ(data.find("etm_test_loop"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, exclude_perf_option) { ASSERT_TRUE(RunInjectCmd({"--exclude-perf"}, nullptr)); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, output_option) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -100,6 +104,7 @@ TEST(cmd_inject, output_option) { CheckMatchingExpectedData("perf_inject.data", autofdo_data); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, skip_empty_output_file) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -110,6 +115,7 @@ TEST(cmd_inject, skip_empty_output_file) { tmpfile.DoNotRemove(); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, inject_kernel_data) { const std::string recording_file = GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_kernel.data"); @@ -132,6 +138,7 @@ TEST(cmd_inject, inject_kernel_data) { ASSERT_EQ(output, autofdo_output); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, unformatted_trace) { std::string data; std::string perf_with_unformatted_trace = @@ -142,6 +149,7 @@ TEST(cmd_inject, unformatted_trace) { CheckMatchingExpectedData("perf_inject.data", data); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, multiple_input_files) { std::string data; std::string perf_data = GetTestData(PERF_DATA_ETM_TEST_LOOP); @@ -165,6 +173,7 @@ TEST(cmd_inject, multiple_input_files) { ASSERT_NE(data.find("106c->1074:200"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, merge_branch_list_files) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -178,6 +187,7 @@ TEST(cmd_inject, merge_branch_list_files) { ASSERT_NE(autofdo_data.find("106c->1074:200"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, report_warning_when_overflow) { CapturedStderr capture; std::vector<std::unique_ptr<TemporaryFile>> branch_list_files; @@ -217,6 +227,7 @@ TEST(cmd_inject, report_warning_when_overflow) { ASSERT_NE(autofdo_data.find("106c->1074:18446744073709551615"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, accept_missing_aux_data) { // Recorded with "-e cs-etm:u --user-buffer-size 64k sleep 1". std::string perf_data = GetTestData("etm/perf_with_missing_aux_data.data"); @@ -225,6 +236,7 @@ TEST(cmd_inject, accept_missing_aux_data) { ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-i", perf_data, "-o", tmpfile.path})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, read_lbr_data) { // Convert perf.data to AutoFDO text format. std::string perf_data_path = GetTestData("lbr/perf_lbr.data"); @@ -264,6 +276,7 @@ TEST(cmd_inject, read_lbr_data) { ASSERT_NE(data.find("194d->1940:706"), data.npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_inject, inject_small_binary) { // etm_test_loop_small, a binary compiled with // "-Wl,-z,noseparate-code", where the file is smaller than its text diff --git a/simpleperf/cmd_kmem_test.cpp b/simpleperf/cmd_kmem_test.cpp index b34f6148..7c66be41 100644 --- a/simpleperf/cmd_kmem_test.cpp +++ b/simpleperf/cmd_kmem_test.cpp @@ -85,15 +85,18 @@ static bool RunKmemRecordCmd(std::vector<std::string> v, const char* output_file return KmemCmd()->Run(v); } +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, record_slab) { TEST_IN_ROOT(ASSERT_TRUE(RunKmemRecordCmd({"--slab"}))); } +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, record_fp_callchain_sampling) { TEST_IN_ROOT(ASSERT_TRUE(RunKmemRecordCmd({"--slab", "-g"}))); TEST_IN_ROOT(ASSERT_TRUE(RunKmemRecordCmd({"--slab", "--call-graph", "fp"}))); } +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, record_and_report) { TemporaryFile tmp_file; TEST_IN_ROOT({ @@ -104,6 +107,7 @@ TEST(kmem_cmd, record_and_report) { }); } +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, record_and_report_callgraph) { TemporaryFile tmp_file; TEST_IN_ROOT({ @@ -116,6 +120,7 @@ TEST(kmem_cmd, record_and_report_callgraph) { #endif +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, report) { ReportResult result; KmemReportFile(PERF_DATA_WITH_KMEM_SLAB_CALLGRAPH_RECORD, {}, &result); @@ -124,6 +129,7 @@ TEST(kmem_cmd, report) { ASSERT_NE(result.content.find("__alloc_skb"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, report_all_sort_options) { ReportResult result; KmemReportFile( @@ -134,6 +140,7 @@ TEST(kmem_cmd, report_all_sort_options) { ASSERT_NE(result.content.find("GfpFlags"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(kmem_cmd, report_callgraph) { ReportResult result; KmemReportFile(PERF_DATA_WITH_KMEM_SLAB_CALLGRAPH_RECORD, {"-g"}, &result); diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp index 963259d1..926b7f7e 100644 --- a/simpleperf/cmd_list.cpp +++ b/simpleperf/cmd_list.cpp @@ -246,8 +246,8 @@ static void PrintRawEventTypes(const std::string& type_desc) { } static bool IsEventTypeSupported(const EventType& event_type) { - // Because PMU events are provided by kernel, we assume it's supported. - if (event_type.IsPmuEvent()) { + // PMU and tracepoint events are provided by kernel. So we assume they're supported. + if (event_type.IsPmuEvent() || event_type.IsTracepointEvent()) { return true; } perf_event_attr attr = CreateDefaultPerfEventAttr(event_type); diff --git a/simpleperf/cmd_list_test.cpp b/simpleperf/cmd_list_test.cpp index e2f804da..940f7595 100644 --- a/simpleperf/cmd_list_test.cpp +++ b/simpleperf/cmd_list_test.cpp @@ -21,6 +21,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 class ListCommandTest : public ::testing::Test { protected: virtual void SetUp() { @@ -31,22 +32,27 @@ class ListCommandTest : public ::testing::Test { std::unique_ptr<Command> list_cmd; }; +// @CddTest = 6.1/C-0-2 TEST_F(ListCommandTest, no_options) { ASSERT_TRUE(list_cmd->Run({})); } +// @CddTest = 6.1/C-0-2 TEST_F(ListCommandTest, one_option) { ASSERT_TRUE(list_cmd->Run({"sw"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ListCommandTest, multiple_options) { ASSERT_TRUE(list_cmd->Run({"hw", "tracepoint"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ListCommandTest, show_features_option) { ASSERT_TRUE(list_cmd->Run({"--show-features"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ListCommandTest, pmu_option) { ASSERT_TRUE(list_cmd->Run({"pmu"})); } diff --git a/simpleperf/cmd_merge_test.cpp b/simpleperf/cmd_merge_test.cpp index 482d0893..1a60c507 100644 --- a/simpleperf/cmd_merge_test.cpp +++ b/simpleperf/cmd_merge_test.cpp @@ -46,6 +46,7 @@ static std::string GetReport(const std::string& record_file) { return data; } +// @CddTest = 6.1/C-0-2 TEST(merge_cmd, input_output_options) { // missing arguments ASSERT_FALSE(MergeCmd()->Run({})); @@ -63,6 +64,7 @@ TEST(merge_cmd, input_output_options) { ASSERT_TRUE(MergeCmd()->Run({"-i", input_file, "-i", input_file, "-o", tmpfile.path})); } +// @CddTest = 6.1/C-0-2 TEST(merge_cmd, merge_two_files) { std::string input_file1 = GetTestData("perf_merge1.data"); std::string input_file2 = GetTestData("perf_merge2.data"); diff --git a/simpleperf/cmd_monitor_test.cpp b/simpleperf/cmd_monitor_test.cpp index 963a9808..8264c966 100644 --- a/simpleperf/cmd_monitor_test.cpp +++ b/simpleperf/cmd_monitor_test.cpp @@ -59,15 +59,18 @@ static ::testing::AssertionResult RunMonitorCmd(std::vector<std::string> v, std: return (result ? ::testing::AssertionSuccess() : ::testing::AssertionFailure()); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, no_options) { std::string output; ASSERT_FALSE(RunMonitorCmd({}, output)); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, no_event) { ASSERT_FALSE(MonitorCmd()->Run({"-a", "--duration", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, global) { TEST_REQUIRE_ROOT(); std::string output; @@ -75,6 +78,7 @@ TEST(monitor_cmd, global) { ASSERT_GT(output.size(), 0); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, no_perf) { TEST_REQUIRE_ROOT(); std::string output; @@ -82,6 +86,7 @@ TEST(monitor_cmd, no_perf) { ASSERT_GT(output.size(), 0); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, with_callchain) { TEST_REQUIRE_ROOT(); std::string output; @@ -89,6 +94,7 @@ TEST(monitor_cmd, with_callchain) { ASSERT_GT(output.size(), 0); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, with_callchain_fp) { TEST_REQUIRE_ROOT(); std::string output; @@ -96,6 +102,7 @@ TEST(monitor_cmd, with_callchain_fp) { ASSERT_GT(output.size(), 0); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, with_callchain_dwarf) { TEST_REQUIRE_ROOT(); std::string output; @@ -103,18 +110,21 @@ TEST(monitor_cmd, with_callchain_dwarf) { ASSERT_GT(output.size(), 0); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, frequency) { TEST_REQUIRE_ROOT(); std::string output; ASSERT_TRUE(RunMonitorCmd({"-a", "-f", "1"}, output)); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, count) { TEST_REQUIRE_ROOT(); std::string output; ASSERT_TRUE(RunMonitorCmd({"-a", "-c", "10000000"}, output)); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, cpu_percent) { TEST_REQUIRE_ROOT(); std::string output; @@ -124,6 +134,7 @@ TEST(monitor_cmd, cpu_percent) { ASSERT_FALSE(RunMonitorCmd({"-a", "--cpu-percent", "101"}, output)); } +// @CddTest = 6.1/C-0-2 TEST(monitor_cmd, record_filter_options) { TEST_REQUIRE_ROOT(); std::string output; diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 40582045..cb9ad884 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -22,6 +22,7 @@ #include <sys/utsname.h> #include <time.h> #include <unistd.h> +#include <chrono> #include <filesystem> #include <optional> #include <set> @@ -111,8 +112,8 @@ static constexpr size_t DEFAULT_CALL_CHAIN_JOINER_CACHE_SIZE = 8 * kMegabyte; static constexpr size_t kDefaultAuxBufferSize = 4 * kMegabyte; // On Pixel 3, it takes about 1ms to enable ETM, and 16-40ms to disable ETM and copy 4M ETM data. -// So make default period to 100ms. -static constexpr double kDefaultEtmDataFlushPeriodInSec = 0.1; +// So make default interval to 100ms. +static constexpr uint32_t kDefaultEtmDataFlushIntervalInMs = 100; struct TimeStat { uint64_t prepare_recording_time = 0; @@ -316,6 +317,8 @@ RECORD_FILTER_OPTION_HELP_MSG_FOR_RECORDING "--record-timestamp Generate timestamp packets in ETM stream.\n" "--record-cycles Generate cycle count packets in ETM stream.\n" "--cycle-threshold <threshold> Set cycle count counter threshold for ETM cycle count packets.\n" +"--etm-flush-interval <interval> Set the interval between ETM data flushes from the ETR buffer\n" +" to the perf event buffer (in milliseconds). Default is 100 ms.\n" "\n" "Other options:\n" "--exit-with-parent Stop recording when the thread starting simpleperf dies.\n" @@ -480,6 +483,7 @@ RECORD_FILTER_OPTION_HELP_MSG_FOR_RECORDING std::unique_ptr<ETMBranchListGenerator> etm_branch_list_generator_; std::unique_ptr<RegEx> binary_name_regex_; + std::chrono::milliseconds etm_flush_interval_{kDefaultEtmDataFlushIntervalInMs}; }; std::string RecordCommand::LongHelpString() const { @@ -631,7 +635,7 @@ bool RecordCommand::PrepareRecording(Workload* workload) { } else { need_to_check_targets = true; } - if (delay_in_ms_ != 0) { + if (delay_in_ms_ != 0 || event_selection_set_.HasAuxTrace()) { event_selection_set_.SetEnableCondition(false, false); } @@ -755,6 +759,12 @@ bool RecordCommand::PrepareRecording(Workload* workload) { } } if (event_selection_set_.HasAuxTrace()) { + // ETM events can only be enabled successfully after MmapEventFiles(). + if (delay_in_ms_ == 0 && !event_selection_set_.IsEnabledOnExec()) { + if (!event_selection_set_.EnableETMEvents()) { + return false; + } + } // ETM data is dumped to kernel buffer only when there is no thread traced by ETM. It happens // either when all monitored threads are scheduled off cpu, or when all etm perf events are // disabled. @@ -762,10 +772,9 @@ bool RecordCommand::PrepareRecording(Workload* workload) { // makes less than expected data, especially in system wide recording. So add a periodic event // to flush etm data by temporarily disable all perf events. auto etm_flush = [this]() { - return event_selection_set_.SetEnableEvents(false) && - event_selection_set_.SetEnableEvents(true); + return event_selection_set_.DisableETMEvents() && event_selection_set_.EnableETMEvents(); }; - if (!loop->AddPeriodicEvent(SecondToTimeval(kDefaultEtmDataFlushPeriodInSec), etm_flush)) { + if (!loop->AddPeriodicEvent(SecondToTimeval(etm_flush_interval_.count() / 1000.0), etm_flush)) { return false; } @@ -800,6 +809,12 @@ bool RecordCommand::DoRecording(Workload* workload) { return false; } time_stat_.stop_recording_time = GetSystemClock(); + if (event_selection_set_.HasAuxTrace()) { + // Disable ETM events to flush the last ETM data. + if (!event_selection_set_.DisableETMEvents()) { + return false; + } + } if (!event_selection_set_.SyncKernelBuffer()) { return false; } @@ -1041,6 +1056,10 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, if (options.PullBoolValue("--decode-etm")) { etm_branch_list_generator_ = ETMBranchListGenerator::Create(system_wide_collection_); } + uint32_t interval = 0; + if (options.PullUintValue("--etm-flush-interval", &interval) && interval != 0) { + etm_flush_interval_ = std::chrono::milliseconds(interval); + } if (options.PullBoolValue("--record-timestamp")) { ETMRecorder& recorder = ETMRecorder::GetInstance(); diff --git a/simpleperf/cmd_record_impl.h b/simpleperf/cmd_record_impl.h index 29f44809..e8561636 100644 --- a/simpleperf/cmd_record_impl.h +++ b/simpleperf/cmd_record_impl.h @@ -51,6 +51,8 @@ inline const OptionFormatMap& GetRecordCmdOptionFormats() { {"--cycle-threshold", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}}, {"--decode-etm", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}}, {"--delay", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}}, + {"--etm-flush-interval", + {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}}, {"--record-timestamp", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}}, {"--record-cycles", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}}, {"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}}, diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 345a128b..898d8756 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -85,10 +85,12 @@ static bool RunRecordCmd(std::vector<std::string> v, const char* output_file = n return RecordCmd()->Run(v); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, no_options) { ASSERT_TRUE(RunRecordCmd({})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, system_wide_option) { TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"}))); } @@ -115,16 +117,19 @@ static void CheckEventType(const std::string& record_file, const std::string& ev FAIL(); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, sample_period_option) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({"-c", "100000"}, tmpfile.path)); CheckEventType(tmpfile.path, GetDefaultEvent(), 100000u, 0); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, event_option) { ASSERT_TRUE(RunRecordCmd({"-e", "cpu-clock"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, freq_option) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({"-f", "99"}, tmpfile.path)); @@ -134,6 +139,7 @@ TEST(record_cmd, freq_option) { ASSERT_FALSE(RunRecordCmd({"-f", std::to_string(UINT_MAX)})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, multiple_freq_or_sample_period_option) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({"-f", "99", "-e", "task-clock", "-c", "1000000", "-e", "cpu-clock"}, @@ -142,11 +148,13 @@ TEST(record_cmd, multiple_freq_or_sample_period_option) { CheckEventType(tmpfile.path, "cpu-clock", 1000000u, 0u); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, output_file_option) { TemporaryFile tmpfile; ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-e", GetDefaultEvent(), "sleep", SLEEP_SEC})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, dump_kernel_mmap) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({}, tmpfile.path)); @@ -167,6 +175,7 @@ TEST(record_cmd, dump_kernel_mmap) { ASSERT_TRUE(have_kernel_mmap); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, dump_build_id_feature) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({}, tmpfile.path)); @@ -177,10 +186,12 @@ TEST(record_cmd, dump_build_id_feature) { ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, tracepoint_event) { TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"}))); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, rN_event) { TEST_REQUIRE_HW_COUNTER(); OMIT_TEST_ON_NON_NATIVE_ABIS(); @@ -211,6 +222,7 @@ TEST(record_cmd, rN_event) { ASSERT_EQ(event_number, attrs[0].attr.config); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, branch_sampling) { TEST_REQUIRE_HW_COUNTER(); if (IsBranchSamplingSupported()) { @@ -225,14 +237,17 @@ TEST(record_cmd, branch_sampling) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, event_modifier) { ASSERT_TRUE(RunRecordCmd({"-e", GetDefaultEvent() + std::string(":u")})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, fp_callchain_sampling) { ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, fp_callchain_sampling_warning_on_arm) { if (GetTargetArch() != ARCH_ARM) { GTEST_LOG_(INFO) << "This test does nothing as it only tests on arm arch."; @@ -245,10 +260,12 @@ TEST(record_cmd, fp_callchain_sampling_warning_on_arm) { testing::ExitedWithCode(0), "doesn't work well on arm"); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, system_wide_fp_callchain_sampling) { TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"}))); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, dwarf_callchain_sampling) { OMIT_TEST_ON_NON_NATIVE_ABIS(); ASSERT_TRUE(IsDwarfCallChainSamplingSupported()); @@ -270,12 +287,14 @@ TEST(record_cmd, dwarf_callchain_sampling) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, system_wide_dwarf_callchain_sampling) { OMIT_TEST_ON_NON_NATIVE_ABIS(); ASSERT_TRUE(IsDwarfCallChainSamplingSupported()); TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, no_unwind_option) { OMIT_TEST_ON_NON_NATIVE_ABIS(); ASSERT_TRUE(IsDwarfCallChainSamplingSupported()); @@ -283,6 +302,7 @@ TEST(record_cmd, no_unwind_option) { ASSERT_FALSE(RunRecordCmd({"--no-unwind"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, post_unwind_option) { OMIT_TEST_ON_NON_NATIVE_ABIS(); ASSERT_TRUE(IsDwarfCallChainSamplingSupported()); @@ -294,6 +314,7 @@ TEST(record_cmd, post_unwind_option) { ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf", "--post-unwind=no"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, existing_processes) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(2, &workloads); @@ -302,6 +323,7 @@ TEST(record_cmd, existing_processes) { ASSERT_TRUE(RunRecordCmd({"-p", pid_list})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, existing_threads) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(2, &workloads); @@ -311,17 +333,20 @@ TEST(record_cmd, existing_threads) { ASSERT_TRUE(RunRecordCmd({"-t", tid_list})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, no_monitored_threads) { TemporaryFile tmpfile; ASSERT_FALSE(RecordCmd()->Run({"-o", tmpfile.path})); ASSERT_FALSE(RecordCmd()->Run({"-o", tmpfile.path, ""})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, more_than_one_event_types) { ASSERT_TRUE(RunRecordCmd({"-e", "task-clock,cpu-clock"})); ASSERT_TRUE(RunRecordCmd({"-e", "task-clock", "-e", "cpu-clock"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, mmap_page_option) { ASSERT_TRUE(RunRecordCmd({"-m", "1"})); ASSERT_FALSE(RunRecordCmd({"-m", "0"})); @@ -345,6 +370,7 @@ static void CheckKernelSymbol(const std::string& path, bool need_kallsyms, bool* *success = true; } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, kernel_symbol) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({"--no-dump-symbols"}, tmpfile.path)); @@ -390,6 +416,7 @@ static bool CheckDumpedSymbols(const std::string& path, bool allow_dumped_symbol return true; } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, no_dump_symbols) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({}, tmpfile.path)); @@ -408,6 +435,7 @@ TEST(record_cmd, no_dump_symbols) { ASSERT_TRUE(CheckDumpedSymbols(tmpfile.path, false)); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, dump_kernel_symbols) { TEST_REQUIRE_ROOT(); TemporaryFile tmpfile; @@ -423,6 +451,7 @@ TEST(record_cmd, dump_kernel_symbols) { ASSERT_TRUE(has_kernel_symbols); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, group_option) { ASSERT_TRUE(RunRecordCmd({"--group", "task-clock,cpu-clock", "-m", "16"})); ASSERT_TRUE( @@ -430,10 +459,12 @@ TEST(record_cmd, group_option) { "--group", "task-clock:k,cpu-clock:k", "-m", "16"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, symfs_option) { ASSERT_TRUE(RunRecordCmd({"--symfs", "/"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, duration_option) { TemporaryFile tmpfile; ASSERT_TRUE(RecordCmd()->Run({"--duration", "1.2", "-p", std::to_string(getpid()), "-o", @@ -442,6 +473,7 @@ TEST(record_cmd, duration_option) { {"--duration", "1", "-o", tmpfile.path, "-e", GetDefaultEvent(), "sleep", "2"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, support_modifier_for_clock_events) { for (const std::string& e : {"cpu-clock", "task-clock"}) { for (const std::string& m : {"u", "k"}) { @@ -450,6 +482,7 @@ TEST(record_cmd, support_modifier_for_clock_events) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, handle_SIGHUP) { TemporaryFile tmpfile; int pipefd[2]; @@ -470,6 +503,7 @@ TEST(record_cmd, handle_SIGHUP) { ASSERT_STREQ(data, "STARTED"); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, stop_when_no_more_targets) { TemporaryFile tmpfile; std::atomic<int> tid(0); @@ -484,6 +518,7 @@ TEST(record_cmd, stop_when_no_more_targets) { {"-o", tmpfile.path, "-t", std::to_string(tid), "--in-app", "-e", GetDefaultEvent()})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, donot_stop_when_having_targets) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(1, &workloads); @@ -496,6 +531,7 @@ TEST(record_cmd, donot_stop_when_having_targets) { ASSERT_GT(end_time_in_ns - start_time_in_ns, static_cast<uint64_t>(2e9)); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, start_profiling_fd_option) { int pipefd[2]; ASSERT_EQ(0, pipe(pipefd)); @@ -514,6 +550,7 @@ TEST(record_cmd, start_profiling_fd_option) { ASSERT_EQ("STARTED", s); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_meta_info_feature) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({}, tmpfile.path)); @@ -530,6 +567,7 @@ TEST(record_cmd, record_meta_info_feature) { } // See http://b/63135835. +// @CddTest = 6.1/C-0-2 TEST(record_cmd, cpu_clock_for_a_long_time) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(1, &workloads); @@ -539,6 +577,7 @@ TEST(record_cmd, cpu_clock_for_a_long_time) { RecordCmd()->Run({"-e", "cpu-clock", "-o", tmpfile.path, "-p", pid, "--duration", "3"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, dump_regs_for_tracepoint_events) { TEST_REQUIRE_HOST_ROOT(); TEST_REQUIRE_TRACEPOINT_EVENTS(); @@ -549,6 +588,7 @@ TEST(record_cmd, dump_regs_for_tracepoint_events) { ASSERT_TRUE(IsDumpingRegsForTracepointEventsSupported()); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, trace_offcpu_option) { // On linux host, we need root privilege to read tracepoint events. TEST_REQUIRE_HOST_ROOT(); @@ -574,10 +614,12 @@ TEST(record_cmd, trace_offcpu_option) { ASSERT_FALSE(RunRecordCmd({"--trace-offcpu", "-e", "cpu-clock,task-clock"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, exit_with_parent_option) { ASSERT_TRUE(RunRecordCmd({"--exit-with-parent"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, use_cmd_exit_code_option) { TemporaryFile tmpfile; int exit_code; @@ -590,6 +632,7 @@ TEST(record_cmd, use_cmd_exit_code_option) { ASSERT_NE(exit_code, 0); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, clockid_option) { if (!IsSettingClockIdSupported()) { ASSERT_FALSE(RunRecordCmd({"--clockid", "monotonic"})); @@ -603,6 +646,7 @@ TEST(record_cmd, clockid_option) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, generate_samples_by_hw_counters) { TEST_REQUIRE_HW_COUNTER(); std::vector<std::string> events = {"cpu-cycles", "instructions"}; @@ -622,16 +666,19 @@ TEST(record_cmd, generate_samples_by_hw_counters) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, callchain_joiner_options) { ASSERT_TRUE(RunRecordCmd({"--no-callchain-joiner"})); ASSERT_TRUE(RunRecordCmd({"--callchain-joiner-min-matching-nodes", "2"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, dashdash) { TemporaryFile tmpfile; ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-e", GetDefaultEvent(), "--", "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, size_limit_option) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(1, &workloads); @@ -646,6 +693,7 @@ TEST(record_cmd, size_limit_option) { ASSERT_FALSE(RunRecordCmd({"--size-limit", "0"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, support_mmap2) { // mmap2 is supported in kernel >= 3.16. If not supported, please cherry pick below kernel // patches: @@ -654,6 +702,7 @@ TEST(record_cmd, support_mmap2) { ASSERT_TRUE(IsMmap2Supported()); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, kernel_bug_making_zero_dyn_size) { // Test a kernel bug that makes zero dyn_size in kernel < 3.13. If it fails, please cherry pick // below kernel patch: 0a196848ca365e perf: Fix arch_perf_out_copy_user default @@ -680,6 +729,7 @@ TEST(record_cmd, kernel_bug_making_zero_dyn_size) { ASSERT_TRUE(has_sample); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, kernel_bug_making_zero_dyn_size_for_kernel_samples) { // Test a kernel bug that makes zero dyn_size for syscalls of 32-bit applications in 64-bit // kernels. If it fails, please cherry pick below kernel patch: @@ -709,6 +759,7 @@ TEST(record_cmd, kernel_bug_making_zero_dyn_size_for_kernel_samples) { ASSERT_TRUE(has_sample); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, cpu_percent_option) { ASSERT_TRUE(RunRecordCmd({"--cpu-percent", "50"})); ASSERT_FALSE(RunRecordCmd({"--cpu-percent", "0"})); @@ -741,6 +792,11 @@ class RecordingAppHelper { }; ProcessSymbolsInPerfDataFile(GetDataPath(), callback); if (!success) { + if (IsInEmulator() && !HasSample()) { + // In emulator, the monitored app may not have a chance to run. + GTEST_LOG_(INFO) << "No samples are recorded. Skip checking symbols."; + return true; + } DumpData(); } return success; @@ -751,6 +807,24 @@ class RecordingAppHelper { std::string GetDataPath() const { return perf_data_file_.path; } private: + bool HasSample() { + std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(GetDataPath()); + if (!reader) { + return false; + } + bool has_sample = false; + auto process_record = [&](std::unique_ptr<Record> r) { + if (r->type() == PERF_RECORD_SAMPLE) { + has_sample = true; + } + return true; + }; + if (!reader->ReadDataSection(process_record)) { + return false; + } + return has_sample; + } + AppHelper app_helper_; TemporaryFile perf_data_file_; }; @@ -790,6 +864,7 @@ static void TestRecordingApps(const std::string& app_name, const std::string& ap } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, app_option_for_debuggable_app) { OMIT_TEST_ON_NON_NATIVE_ABIS(); TEST_REQUIRE_APPS(); @@ -799,6 +874,7 @@ TEST(record_cmd, app_option_for_debuggable_app) { TestRecordingApps("com.android.simpleperf.debuggable", "debuggable"); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, app_option_for_profileable_app) { OMIT_TEST_ON_NON_NATIVE_ABIS(); TEST_REQUIRE_APPS(); @@ -828,6 +904,7 @@ static void RecordJavaApp(RecordingAppHelper& helper) { } #endif // defined(__ANDROID__) +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_java_app) { #if defined(__ANDROID__) OMIT_TEST_ON_NON_NATIVE_ABIS(); @@ -858,6 +935,7 @@ TEST(record_cmd, record_java_app) { #endif } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_native_app) { #if defined(__ANDROID__) // In case of non-native ABI guest symbols are never directly executed, thus @@ -893,6 +971,7 @@ TEST(record_cmd, record_native_app) { #endif } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, check_trampoline_after_art_jni_methods) { // Test if art jni methods are called by art_jni_trampoline. #if defined(__ANDROID__) @@ -959,10 +1038,12 @@ TEST(record_cmd, check_trampoline_after_art_jni_methods) { #endif } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, no_cut_samples_option) { ASSERT_TRUE(RunRecordCmd({"--no-cut-samples"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, cs_etm_event) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; @@ -997,6 +1078,7 @@ TEST(record_cmd, cs_etm_event) { ASSERT_TRUE(has_aux); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, cs_etm_system_wide) { TEST_REQUIRE_ROOT(); if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { @@ -1006,6 +1088,7 @@ TEST(record_cmd, cs_etm_system_wide) { ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "-a"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, aux_buffer_size_option) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; @@ -1018,6 +1101,7 @@ TEST(record_cmd, aux_buffer_size_option) { ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "12k"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, addr_filter_option) { TEST_REQUIRE_HW_COUNTER(); if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { @@ -1078,6 +1162,7 @@ TEST(record_cmd, addr_filter_option) { ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--addr-filter", filter})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, decode_etm_option) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; @@ -1087,6 +1172,7 @@ TEST(record_cmd, decode_etm_option) { ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--decode-etm", "--exclude-perf"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_timestamp) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; @@ -1095,6 +1181,7 @@ TEST(record_cmd, record_timestamp) { ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--record-timestamp"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_cycles) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; @@ -1103,15 +1190,16 @@ TEST(record_cmd, record_cycles) { ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--record-cycles"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, cycle_threshold) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; return; } - ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--record-cycles", - "--cycle-threshold", "8"})); + ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--record-cycles", "--cycle-threshold", "8"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, binary_option) { if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; @@ -1120,6 +1208,16 @@ TEST(record_cmd, binary_option) { ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--decode-etm", "--binary", ".*"})); } +// @CddTest = 6.1/C-0-2 +TEST(record_cmd, etm_flush_interval_option) { + if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) { + GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; + return; + } + ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--etm-flush-interval", "10"})); +} + +// @CddTest = 6.1/C-0-2 TEST(record_cmd, pmu_event_option) { TEST_REQUIRE_PMU_COUNTER(); TEST_REQUIRE_HW_COUNTER(); @@ -1135,6 +1233,7 @@ TEST(record_cmd, pmu_event_option) { TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-e", event_string}))); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, exclude_perf_option) { ASSERT_TRUE(RunRecordCmd({"--exclude-perf"})); if (IsRoot()) { @@ -1155,6 +1254,7 @@ TEST(record_cmd, exclude_perf_option) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, tp_filter_option) { TEST_REQUIRE_HOST_ROOT(); TEST_REQUIRE_TRACEPOINT_EVENTS(); @@ -1175,6 +1275,7 @@ TEST(record_cmd, tp_filter_option) { } } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, ParseAddrFilterOption) { auto option_to_str = [](const std::string& option) { auto filters = ParseAddrFilterOption(option); @@ -1207,6 +1308,7 @@ TEST(record_cmd, ParseAddrFilterOption) { ASSERT_EQ(option_to_str("start 0x12345678,stop 0x1234567a"), "start 0x12345678,stop 0x1234567a"); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, kprobe_option) { TEST_REQUIRE_ROOT(); EventSelectionSet event_selection_set(false); @@ -1221,6 +1323,7 @@ TEST(record_cmd, kprobe_option) { ASSERT_TRUE(RunRecordCmd({"--group", "kprobes:do_sys_openat2"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_filter_options) { ASSERT_TRUE( RunRecordCmd({"--exclude-pid", "1,2", "--exclude-tid", "3,4", "--exclude-process-name", @@ -1230,6 +1333,7 @@ TEST(record_cmd, record_filter_options) { "processB", "--include-thread-name", "threadB", "--include-uid", "5,6"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, keep_failed_unwinding_result_option) { OMIT_TEST_ON_NON_NATIVE_ABIS(); std::vector<std::unique_ptr<Workload>> workloads; @@ -1239,6 +1343,7 @@ TEST(record_cmd, keep_failed_unwinding_result_option) { {"-p", pid, "-g", "--keep-failed-unwinding-result", "--keep-failed-unwinding-debug-info"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, kernel_address_warning) { TEST_REQUIRE_NON_ROOT(); const std::string warning_msg = "Access to kernel symbol addresses is restricted."; @@ -1263,6 +1368,7 @@ TEST(record_cmd, kernel_address_warning) { ASSERT_EQ(output.find(warning_msg, pos + warning_msg.size()), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, add_meta_info_option) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({"--add-meta-info", "key1=value1", "--add-meta-info", "key2=value2"}, @@ -1284,6 +1390,7 @@ TEST(record_cmd, add_meta_info_option) { ASSERT_FALSE(RunRecordCmd({"--add-meta-info", "=value1"}, tmpfile.path)); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, device_meta_info) { #if defined(__ANDROID__) TemporaryFile tmpfile; @@ -1303,6 +1410,7 @@ TEST(record_cmd, device_meta_info) { #endif } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, add_counter_option) { TEST_REQUIRE_HW_COUNTER(); TemporaryFile tmpfile; @@ -1324,10 +1432,12 @@ TEST(record_cmd, add_counter_option) { ASSERT_TRUE(has_sample); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, user_buffer_size_option) { ASSERT_TRUE(RunRecordCmd({"--user-buffer-size", "256M"})); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, record_process_name) { TemporaryFile tmpfile; ASSERT_TRUE(RecordCmd()->Run({"-e", GetDefaultEvent(), "-o", tmpfile.path, "sleep", SLEEP_SEC})); @@ -1346,6 +1456,7 @@ TEST(record_cmd, record_process_name) { ASSERT_TRUE(has_comm); } +// @CddTest = 6.1/C-0-2 TEST(record_cmd, delay_option) { TemporaryFile tmpfile; ASSERT_TRUE(RecordCmd()->Run( diff --git a/simpleperf/cmd_report_sample_test.cpp b/simpleperf/cmd_report_sample_test.cpp index d71dce2f..b544f7b5 100644 --- a/simpleperf/cmd_report_sample_test.cpp +++ b/simpleperf/cmd_report_sample_test.cpp @@ -29,16 +29,19 @@ static std::unique_ptr<Command> ReportSampleCmd() { return CreateCommandInstance("report-sample"); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, text) { ASSERT_TRUE(ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_SYMBOLS)})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, output_option) { TemporaryFile tmpfile; ASSERT_TRUE( ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_SYMBOLS), "-o", tmpfile.path})); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, show_callchain_option) { TemporaryFile tmpfile; ASSERT_TRUE(ReportSampleCmd()->Run( @@ -58,6 +61,7 @@ static void GetProtobufReport(const std::string& test_data_file, std::string* pr ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, protobuf_report)); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, protobuf_option) { std::string data; GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data); @@ -66,6 +70,7 @@ TEST(cmd_report_sample, protobuf_option) { ASSERT_NE(data.find("file:"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, no_skipped_file_id) { std::string data; GetProtobufReport(PERF_DATA_WITH_WRONG_IP_IN_CALLCHAIN, &data); @@ -73,12 +78,14 @@ TEST(cmd_report_sample, no_skipped_file_id) { ASSERT_EQ(data.find("unknown"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, sample_has_event_count) { std::string data; GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data); ASSERT_NE(data.find("event_count:"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, has_thread_record) { std::string data; GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data); @@ -86,6 +93,7 @@ TEST(cmd_report_sample, has_thread_record) { ASSERT_NE(data.find("thread_name: t2"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, trace_offcpu) { std::string data; GetProtobufReport("perf_with_trace_offcpu_v2.data", &data); @@ -101,6 +109,7 @@ TEST(cmd_report_sample, trace_offcpu) { } } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, have_clear_callchain_end_in_protobuf_output) { std::string data; GetProtobufReport("perf_with_trace_offcpu_v2.data", &data, {"--show-callchain"}); @@ -108,6 +117,7 @@ TEST(cmd_report_sample, have_clear_callchain_end_in_protobuf_output) { ASSERT_EQ(data.find("_start_main"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, app_device_info_in_meta_info) { std::string data; GetProtobufReport("perf_with_meta_info.data", &data); @@ -117,6 +127,7 @@ TEST(cmd_report_sample, app_device_info_in_meta_info) { ASSERT_NE(data.find("android_build_type: userdebug"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, remove_unknown_kernel_symbols) { std::string data; // Test --remove-unknown-kernel-symbols on perf.data with kernel_symbols_available=false. @@ -148,6 +159,7 @@ TEST(cmd_report_sample, remove_unknown_kernel_symbols) { ASSERT_NE(data.find("path: /system/lib64/libc.so"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, show_art_frames_option) { std::string data; GetProtobufReport(PERF_DATA_WITH_INTERPRETER_FRAMES, &data, {"--show-callchain"}); @@ -157,6 +169,7 @@ TEST(cmd_report_sample, show_art_frames_option) { ASSERT_NE(data.find("artMterpAsmInstructionStart"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, show_execution_type_option) { std::string data; GetProtobufReport("perf_display_bitmaps.data", &data, @@ -174,6 +187,7 @@ TEST(cmd_report_sample, show_execution_type_option) { ASSERT_NE(data.find("execution_type: art_method"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, show_symbols_before_and_after_demangle) { std::string data; GetProtobufReport(PERF_DATA_WITH_INTERPRETER_FRAMES, &data, {"--show-callchain"}); @@ -183,6 +197,7 @@ TEST(cmd_report_sample, show_symbols_before_and_after_demangle) { std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, symdir_option) { std::string data; GetProtobufReport(PERF_DATA_FOR_BUILD_ID_CHECK, &data); @@ -192,6 +207,7 @@ TEST(cmd_report_sample, symdir_option) { ASSERT_NE(data.find("symbol: main"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, show_art_jni_methods) { std::string data; GetProtobufReport("perf_display_bitmaps.data", &data, {"--show-callchain"}); @@ -200,6 +216,7 @@ TEST(cmd_report_sample, show_art_jni_methods) { ASSERT_EQ(data.find("art_jni_trampoline"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, show_unwinding_result) { std::string data; GetProtobufReport("perf_with_failed_unwinding_debug_info.data", &data, @@ -207,6 +224,7 @@ TEST(cmd_report_sample, show_unwinding_result) { ASSERT_NE(data.find("error_code: ERROR_INVALID_MAP"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, proguard_mapping_file_option) { std::string data; // Symbols aren't de-obfuscated without proguard mapping file. @@ -226,6 +244,7 @@ TEST(cmd_report_sample, proguard_mapping_file_option) { std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, exclude_include_pid_options) { std::string data; GetProtobufReport("perf_display_bitmaps.data", &data, {"--exclude-pid", "31850"}); @@ -235,6 +254,7 @@ TEST(cmd_report_sample, exclude_include_pid_options) { ASSERT_NE(data.find("thread_id: 31850"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, exclude_include_tid_options) { std::string data; GetProtobufReport("perf_display_bitmaps.data", &data, {"--exclude-tid", "31881"}); @@ -244,6 +264,7 @@ TEST(cmd_report_sample, exclude_include_tid_options) { ASSERT_NE(data.find("thread_id: 31881"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, exclude_include_process_name_options) { std::string data; GetProtobufReport("perf_display_bitmaps.data", &data, @@ -255,6 +276,7 @@ TEST(cmd_report_sample, exclude_include_process_name_options) { ASSERT_NE(data.find("thread_id: 31881"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, exclude_include_thread_name_options) { std::string data; GetProtobufReport("perf_display_bitmaps.data", &data, @@ -266,6 +288,7 @@ TEST(cmd_report_sample, exclude_include_thread_name_options) { ASSERT_NE(data.find("thread_id: 31850"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, filter_file_option) { std::string filter_data = "GLOBAL_BEGIN 684943449406175\n" @@ -278,6 +301,7 @@ TEST(cmd_report_sample, filter_file_option) { ASSERT_EQ(data.find("thread_id: 31850"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(cmd_report_sample, remove_gaps_option) { auto get_sample_count = [](const std::string& s) { size_t count = 0; diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index fcb9fac4..9170beb2 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -37,6 +37,7 @@ static std::unique_ptr<Command> ReportCmd() { return CreateCommandInstance("report"); } +// @CddTest = 6.1/C-0-2 class ReportCommandTest : public ::testing::Test { protected: void Report(const std::string& perf_data, @@ -90,18 +91,21 @@ class ReportCommandTest : public ::testing::Test { bool success; }; +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, no_option) { Report(PERF_DATA); ASSERT_TRUE(success); ASSERT_NE(content.find("GlobalFunc"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) { Report(PERF_DATA_WITH_MINI_DEBUG_INFO); ASSERT_TRUE(success); ASSERT_NE(content.find("GlobalFunc"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, sort_option_pid) { Report(PERF_DATA, {"--sort", "pid"}); ASSERT_TRUE(success); @@ -112,6 +116,7 @@ TEST_F(ReportCommandTest, sort_option_pid) { ASSERT_LT(line_index + 2, lines.size()); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, sort_option_more_than_one) { Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"}); ASSERT_TRUE(success); @@ -127,6 +132,7 @@ TEST_F(ReportCommandTest, sort_option_more_than_one) { ASSERT_EQ(lines[line_index].find("Tid"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, children_option) { Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--sort", "symbol"}); ASSERT_TRUE(success); @@ -171,6 +177,7 @@ static bool CheckCallerMode(std::vector<std::string>& lines) { return found; } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, callgraph_option) { Report(CALLGRAPH_FP_PERF_DATA, {"-g"}); ASSERT_TRUE(success); @@ -208,6 +215,7 @@ static bool AllItemsWithString(std::vector<std::string>& lines, return true; } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, pid_filter_option) { Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid"}); ASSERT_TRUE(success); @@ -228,6 +236,7 @@ TEST_F(ReportCommandTest, pid_filter_option) { ASSERT_NE(content.find("17445"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, wrong_pid_filter_option) { ASSERT_EXIT( { @@ -237,6 +246,7 @@ TEST_F(ReportCommandTest, wrong_pid_filter_option) { testing::ExitedWithCode(1), "invalid pid: bogus"); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, tid_filter_option) { Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid"}); ASSERT_TRUE(success); @@ -250,6 +260,7 @@ TEST_F(ReportCommandTest, tid_filter_option) { ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17445"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, wrong_tid_filter_option) { ASSERT_EXIT( { @@ -259,6 +270,7 @@ TEST_F(ReportCommandTest, wrong_tid_filter_option) { testing::ExitedWithCode(1), "Invalid tid 'bogus'"); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, comm_filter_option) { Report(PERF_DATA, {"--sort", "comm"}); ASSERT_TRUE(success); @@ -272,6 +284,7 @@ TEST_F(ReportCommandTest, comm_filter_option) { ASSERT_TRUE(AllItemsWithString(lines, {"t1", "t2"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, dso_filter_option) { Report(PERF_DATA, {"--sort", "dso"}); ASSERT_TRUE(success); @@ -285,6 +298,7 @@ TEST_F(ReportCommandTest, dso_filter_option) { ASSERT_TRUE(AllItemsWithString(lines, {"/t1", "/t2"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, symbol_filter_option) { Report(PERF_DATA_WITH_SYMBOLS, {"--sort", "symbol"}); ASSERT_TRUE(success); @@ -298,6 +312,7 @@ TEST_F(ReportCommandTest, symbol_filter_option) { ASSERT_TRUE(AllItemsWithString(lines, {"main", "func2(int, int)"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, dso_symbol_filter_with_children_option) { // dso and symbol filter should filter different layers of the callchain separately. Report("perf_display_bitmaps.data", {"--dsos", "/apex/com.android.runtime/lib64/libart.so", @@ -312,6 +327,7 @@ TEST_F(ReportCommandTest, dso_symbol_filter_with_children_option) { ASSERT_NE(content.find("51500000 2500000 MterpInvokeVirtual"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, use_branch_address) { Report(BRANCH_PERF_DATA, {"-b", "--sort", "symbol_from,symbol_to"}); std::set<std::pair<std::string, std::string>> hit_set; @@ -333,6 +349,7 @@ TEST_F(ReportCommandTest, use_branch_address) { hit_set.end()); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { Report(NATIVELIB_IN_APK_PERF_DATA); ASSERT_TRUE(success); @@ -340,6 +357,7 @@ TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { ASSERT_NE(content.find("Func2"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_more_than_one_event_types) { Report(PERF_DATA_WITH_TWO_EVENT_TYPES); ASSERT_TRUE(success); @@ -350,12 +368,14 @@ TEST_F(ReportCommandTest, report_more_than_one_event_types) { ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_kernel_symbol) { Report(PERF_DATA_WITH_KERNEL_SYMBOL); ASSERT_TRUE(success); ASSERT_NE(content.find("perf_event_aux"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_dumped_symbols) { Report(PERF_DATA_WITH_SYMBOLS); ASSERT_TRUE(success); @@ -365,6 +385,7 @@ TEST_F(ReportCommandTest, report_dumped_symbols) { ASSERT_NE(content.find("memcpy"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_dumped_symbols_with_symfs_dir) { // Check if we can report symbols when they appear both in perf.data and symfs dir. Report(PERF_DATA_WITH_SYMBOLS, {"--symfs", GetTestDataDir()}); @@ -372,6 +393,7 @@ TEST_F(ReportCommandTest, report_dumped_symbols_with_symfs_dir) { ASSERT_NE(content.find("main"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_dumped_symbols_with_symdir) { // Check if we can report symbols by specifying symdir. Report(PERF_DATA, {"--symdir", GetTestDataDir()}, false); @@ -379,17 +401,20 @@ TEST_F(ReportCommandTest, report_dumped_symbols_with_symdir) { ASSERT_NE(content.find("GlobalFunc"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_without_symfs_dir) { TemporaryFile tmpfile; ASSERT_TRUE(ReportCmd()->Run({"-i", GetTestData(PERF_DATA), "-o", tmpfile.path})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_sort_vaddr_in_file) { Report(PERF_DATA, {"--sort", "vaddr_in_file"}); ASSERT_TRUE(success); ASSERT_NE(content.find("VaddrInFile"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, check_build_id) { Report(PERF_DATA_FOR_BUILD_ID_CHECK, {"--symfs", GetTestData(CORRECT_SYMFS_FOR_BUILD_ID_CHECK)}); ASSERT_TRUE(success); @@ -409,6 +434,7 @@ TEST_F(ReportCommandTest, check_build_id) { testing::ExitedWithCode(0), "failed to read symbols from /elf_for_build_id_check"); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, no_show_ip_option) { Report(PERF_DATA); ASSERT_TRUE(success); @@ -418,6 +444,7 @@ TEST_F(ReportCommandTest, no_show_ip_option) { ASSERT_NE(content.find("unknown"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, read_elf_file_warning) { ASSERT_EXIT( { @@ -433,11 +460,13 @@ TEST_F(ReportCommandTest, read_elf_file_warning) { testing::ExitedWithCode(0), "failed to read symbols from /elf: File not found"); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) { Report(PERF_DATA_GENERATED_BY_LINUX_PERF); ASSERT_TRUE(success); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, max_stack_and_percent_limit_option) { Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g"}); ASSERT_TRUE(success); @@ -458,6 +487,7 @@ TEST_F(ReportCommandTest, max_stack_and_percent_limit_option) { ASSERT_NE(content.find("89.03"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, percent_limit_option) { Report(PERF_DATA); ASSERT_TRUE(success); @@ -469,16 +499,19 @@ TEST_F(ReportCommandTest, percent_limit_option) { ASSERT_EQ(content.find("3.23%"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, kallsyms_option) { Report(PERF_DATA, {"--kallsyms", GetTestData("kallsyms")}); ASSERT_TRUE(success); ASSERT_NE(content.find("FakeKernelSymbol"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, invalid_perf_data) { ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData(INVALID_PERF_DATA)})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, raw_period_option) { Report(PERF_DATA, {"--raw-period"}); ASSERT_TRUE(success); @@ -486,6 +519,7 @@ TEST_F(ReportCommandTest, raw_period_option) { ASSERT_EQ(content.find('%'), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, full_callgraph_option) { Report(CALLGRAPH_FP_PERF_DATA, {"-g"}); ASSERT_TRUE(success); @@ -495,6 +529,7 @@ TEST_F(ReportCommandTest, full_callgraph_option) { ASSERT_EQ(content.find("skipped in brief callgraph mode"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_offcpu_time) { Report(PERF_DATA_WITH_TRACE_OFFCPU, {"--children"}); ASSERT_TRUE(success); @@ -510,11 +545,13 @@ TEST_F(ReportCommandTest, report_offcpu_time) { ASSERT_TRUE(found); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_big_trace_data) { Report(PERF_DATA_WITH_BIG_TRACE_DATA); ASSERT_TRUE(success); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, csv_option) { Report(PERF_DATA, {"--csv"}); ASSERT_TRUE(success); @@ -525,6 +562,7 @@ TEST_F(ReportCommandTest, csv_option) { ASSERT_NE(content.find("AccEventCount,SelfEventCount,EventName"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, csv_separator_option) { Report(PERF_DATA, {"--csv", "--csv-separator", ";"}); ASSERT_TRUE(success); @@ -532,6 +570,7 @@ TEST_F(ReportCommandTest, csv_separator_option) { ASSERT_NE(content.find(";cpu-cycles"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, dso_path_for_jit_cache) { Report("perf_with_jit_symbol.data", {"--sort", "dso"}); ASSERT_TRUE(success); @@ -543,12 +582,14 @@ TEST_F(ReportCommandTest, dso_path_for_jit_cache) { ASSERT_NE(content.find("[JIT app cache]"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, generic_jit_symbols) { Report("perf_with_generic_git_symbols.data", {"--sort", "symbol"}); ASSERT_TRUE(success); ASSERT_NE(std::string::npos, content.find("generic_jit_symbol_one")); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, cpu_option) { Report("perf.data"); ASSERT_TRUE(success); @@ -565,6 +606,7 @@ TEST_F(ReportCommandTest, cpu_option) { ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData("perf.data"), "--cpu", "-2"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, print_event_count_option) { // Report record file not recorded with --add-counter. Report("perf.data", {"--print-event-count"}); @@ -597,6 +639,7 @@ TEST_F(ReportCommandTest, print_event_count_option) { ->Search(content)); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, exclude_include_pid_options) { Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid", "--exclude-pid", "17441"}); ASSERT_TRUE(success); @@ -606,6 +649,7 @@ TEST_F(ReportCommandTest, exclude_include_pid_options) { ASSERT_TRUE(AllItemsWithString(lines, {"17441"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, exclude_include_tid_options) { Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid", "--exclude-tid", "17441,17443,17444"}); @@ -617,6 +661,7 @@ TEST_F(ReportCommandTest, exclude_include_tid_options) { ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17443", "17444"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, exclude_include_process_name_options) { Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "comm", "--exclude-process-name", "t1"}); ASSERT_TRUE(success); @@ -626,6 +671,7 @@ TEST_F(ReportCommandTest, exclude_include_process_name_options) { ASSERT_TRUE(AllItemsWithString(lines, {"t1"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, exclude_include_thread_name_options) { Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "comm", "--exclude-thread-name", "t1"}); ASSERT_TRUE(success); @@ -635,6 +681,7 @@ TEST_F(ReportCommandTest, exclude_include_thread_name_options) { ASSERT_TRUE(AllItemsWithString(lines, {"t1"})); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, filter_file_option) { std::string filter_data = "GLOBAL_BEGIN 684943449406175\n" @@ -659,6 +706,7 @@ static std::unique_ptr<Command> RecordCmd() { return CreateCommandInstance("record"); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, dwarf_callgraph) { TEST_REQUIRE_HW_COUNTER(); OMIT_TEST_ON_NON_NATIVE_ABIS(); @@ -672,6 +720,7 @@ TEST_F(ReportCommandTest, dwarf_callgraph) { ASSERT_TRUE(success); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) { Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"}); ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos); @@ -680,6 +729,7 @@ TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) { ASSERT_NE(content.find("GlobalFunc"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST_F(ReportCommandTest, exclude_kernel_callchain) { TEST_REQUIRE_HW_COUNTER(); TEST_REQUIRE_HOST_ROOT(); diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp index 86c7ae8c..c47ad729 100644 --- a/simpleperf/cmd_stat_test.cpp +++ b/simpleperf/cmd_stat_test.cpp @@ -36,26 +36,32 @@ static std::unique_ptr<Command> StatCmd() { return CreateCommandInstance("stat"); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, no_options) { ASSERT_TRUE(StatCmd()->Run({"sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, event_option) { ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-clock,task-clock", "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, system_wide_option) { TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}))); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, verbose_option) { ASSERT_TRUE(StatCmd()->Run({"--verbose", "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, tracepoint_event) { TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}))); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, rN_event) { TEST_REQUIRE_HW_COUNTER(); OMIT_TEST_ON_NON_NATIVE_ABIS(); @@ -79,6 +85,7 @@ TEST(stat_cmd, rN_event) { ASSERT_TRUE(StatCmd()->Run({"-e", event_name, "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, pmu_event) { TEST_REQUIRE_PMU_COUNTER(); TEST_REQUIRE_HW_COUNTER(); @@ -96,6 +103,7 @@ TEST(stat_cmd, pmu_event) { TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "-e", event_string, "sleep", "1"}))); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, event_modifier) { TEST_REQUIRE_HW_COUNTER(); ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-cycles:u,cpu-cycles:k", "sleep", "1"})); @@ -121,6 +129,7 @@ void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workl } } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, existing_processes) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(2, &workloads); @@ -129,6 +138,7 @@ TEST(stat_cmd, existing_processes) { ASSERT_TRUE(StatCmd()->Run({"-p", pid_list, "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, existing_threads) { std::vector<std::unique_ptr<Workload>> workloads; CreateProcesses(2, &workloads); @@ -138,11 +148,13 @@ TEST(stat_cmd, existing_threads) { ASSERT_TRUE(StatCmd()->Run({"-t", tid_list, "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, no_monitored_threads) { ASSERT_FALSE(StatCmd()->Run({})); ASSERT_FALSE(StatCmd()->Run({""})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, group_option) { TEST_REQUIRE_HW_COUNTER(); ASSERT_TRUE(StatCmd()->Run({"--group", "cpu-clock,page-faults", "sleep", "1"})); @@ -151,6 +163,7 @@ TEST(stat_cmd, group_option) { "cpu-cycles:k,instructions:k", "sleep", "1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, auto_generated_summary) { TEST_REQUIRE_HW_COUNTER(); TemporaryFile tmp_file; @@ -167,11 +180,13 @@ TEST(stat_cmd, auto_generated_summary) { ASSERT_NE(s.npos, s.find("instructions", pos)); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, duration_option) { ASSERT_TRUE(StatCmd()->Run({"--duration", "1.2", "-p", std::to_string(getpid()), "--in-app"})); ASSERT_TRUE(StatCmd()->Run({"--duration", "1", "sleep", "2"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, interval_option) { TemporaryFile tmp_file; ASSERT_TRUE(StatCmd()->Run( @@ -188,16 +203,19 @@ TEST(stat_cmd, interval_option) { ASSERT_EQ(count, 2UL); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, interval_option_in_system_wide) { TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "--interval", "100", "--duration", "0.3"}))); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, interval_only_values_option) { ASSERT_TRUE(StatCmd()->Run({"--interval", "500", "--interval-only-values", "sleep", "2"})); TEST_IN_ROOT(ASSERT_TRUE( StatCmd()->Run({"-a", "--interval", "100", "--interval-only-values", "--duration", "0.3"}))); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, no_modifier_for_clock_events) { for (const std::string& e : {"cpu-clock", "task-clock"}) { for (const std::string& m : {"u", "k"}) { @@ -207,6 +225,7 @@ TEST(stat_cmd, no_modifier_for_clock_events) { } } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, handle_SIGHUP) { std::thread thread([]() { sleep(1); @@ -216,6 +235,7 @@ TEST(stat_cmd, handle_SIGHUP) { ASSERT_TRUE(StatCmd()->Run({"sleep", "1000000"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, stop_when_no_more_targets) { std::atomic<int> tid(0); std::thread thread([&]() { @@ -228,6 +248,7 @@ TEST(stat_cmd, stop_when_no_more_targets) { ASSERT_TRUE(StatCmd()->Run({"-t", std::to_string(tid), "--in-app"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, sample_rate_should_be_zero) { TEST_REQUIRE_HW_COUNTER(); EventSelectionSet set(true); @@ -244,6 +265,7 @@ TEST(stat_cmd, sample_rate_should_be_zero) { } } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, calculating_cpu_frequency) { TEST_REQUIRE_HW_COUNTER(); CaptureStdout capture; @@ -269,6 +291,7 @@ TEST(stat_cmd, calculating_cpu_frequency) { ASSERT_NEAR(cpu_frequency, calculated_frequency, 1e-3); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, set_comm_in_another_thread) { // Test a kernel bug which was fixed in 3.15. If kernel panic happens, please cherry pick kernel // patch: e041e328c4b41e perf: Fix perf_event_comm() vs. exec() assumption @@ -321,6 +344,7 @@ static void TestStatingApps(const std::string& app_name) { ASSERT_TRUE(StatCmd()->Run({"--app", app_name, "--duration", "3"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, app_option_for_debuggable_app) { TEST_REQUIRE_APPS(); SetRunInAppToolForTesting(true, false); @@ -329,12 +353,14 @@ TEST(stat_cmd, app_option_for_debuggable_app) { TestStatingApps("com.android.simpleperf.debuggable"); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, app_option_for_profileable_app) { TEST_REQUIRE_APPS(); SetRunInAppToolForTesting(false, true); TestStatingApps("com.android.simpleperf.profileable"); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, use_devfreq_counters_option) { #if defined(__ANDROID__) TEST_IN_ROOT(StatCmd()->Run({"--use-devfreq-counters", "sleep", "0.1"})); @@ -343,21 +369,25 @@ TEST(stat_cmd, use_devfreq_counters_option) { #endif } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, per_thread_option) { ASSERT_TRUE(StatCmd()->Run({"--per-thread", "sleep", "0.1"})); TEST_IN_ROOT(StatCmd()->Run({"--per-thread", "-a", "--duration", "0.1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, per_core_option) { ASSERT_TRUE(StatCmd()->Run({"--per-core", "sleep", "0.1"})); TEST_IN_ROOT(StatCmd()->Run({"--per-core", "-a", "--duration", "0.1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, sort_option) { ASSERT_TRUE( StatCmd()->Run({"--per-thread", "--per-core", "--sort", "cpu,count", "sleep", "0.1"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, counter_sum) { PerfCounter counter; counter.value = 1; @@ -382,10 +412,12 @@ TEST(stat_cmd, counter_sum) { ASSERT_EQ(counter.time_running, 6); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, print_hw_counter_option) { ASSERT_TRUE(StatCmd()->Run({"--print-hw-counter"})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, record_different_counters_for_different_cpus) { std::vector<int> online_cpus = GetOnlineCpus(); ASSERT_FALSE(online_cpus.empty()); @@ -412,6 +444,7 @@ TEST(stat_cmd, record_different_counters_for_different_cpus) { ASSERT_TRUE(has_task_clock) << output; } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, kprobe_option) { TEST_REQUIRE_ROOT(); EventSelectionSet event_selection_set(false); @@ -427,6 +460,7 @@ TEST(stat_cmd, kprobe_option) { ASSERT_TRUE(StatCmd()->Run({"--group", "kprobes:do_sys_openat2", "-a", "--duration", SLEEP_SEC})); } +// @CddTest = 6.1/C-0-2 TEST(stat_cmd, tp_filter_option) { TEST_REQUIRE_HOST_ROOT(); TEST_REQUIRE_TRACEPOINT_EVENTS(); @@ -434,6 +468,7 @@ TEST(stat_cmd, tp_filter_option) { {"-e", "sched:sched_switch", "--tp-filter", "prev_comm != sleep", "sleep", SLEEP_SEC})); } +// @CddTest = 6.1/C-0-2 class StatCmdSummaryBuilderTest : public ::testing::Test { protected: struct CounterArg { @@ -485,6 +520,7 @@ class StatCmdSummaryBuilderTest : public ::testing::Test { std::vector<std::string> sort_keys_; }; +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, multiple_events) { AddCounter({.event_id = 0, .value = 1, .time_enabled = 1, .time_running = 1}); AddCounter({.event_id = 1, .value = 2, .time_enabled = 2, .time_running = 2}); @@ -498,6 +534,7 @@ TEST_F(StatCmdSummaryBuilderTest, multiple_events) { ASSERT_NEAR(summaries[1].scale, 1.0, 1e-5); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, default_aggregate) { AddCounter({.tid = 0, .cpu = 0, .value = 1, .time_enabled = 1, .time_running = 1}); AddCounter({.tid = 0, .cpu = 1, .value = 1, .time_enabled = 1, .time_running = 1}); @@ -509,6 +546,7 @@ TEST_F(StatCmdSummaryBuilderTest, default_aggregate) { ASSERT_NEAR(summaries[0].scale, 1.25, 1e-5); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, per_thread_aggregate) { AddCounter({.tid = 0, .cpu = 0, .value = 1, .time_enabled = 1, .time_running = 1}); AddCounter({.tid = 0, .cpu = 1, .value = 1, .time_enabled = 1, .time_running = 1}); @@ -526,6 +564,7 @@ TEST_F(StatCmdSummaryBuilderTest, per_thread_aggregate) { ASSERT_NEAR(summaries[1].scale, 1.0, 1e-5); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, per_core_aggregate) { AddCounter({.tid = 0, .cpu = 0, .value = 1, .time_enabled = 1, .time_running = 1}); AddCounter({.tid = 0, .cpu = 1, .value = 1, .time_enabled = 1, .time_running = 1}); @@ -543,6 +582,7 @@ TEST_F(StatCmdSummaryBuilderTest, per_core_aggregate) { ASSERT_NEAR(summaries[1].scale, 1.5, 1e-5); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, per_thread_core_aggregate) { AddCounter({.tid = 0, .cpu = 0, .value = 1, .time_enabled = 1, .time_running = 1}); AddCounter({.tid = 0, .cpu = 1, .value = 2, .time_enabled = 1, .time_running = 1}); @@ -568,6 +608,7 @@ TEST_F(StatCmdSummaryBuilderTest, per_thread_core_aggregate) { ASSERT_NEAR(summaries[3].scale, 1.0, 1e-5); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, sort_key_count) { sort_keys_ = {"count"}; AddCounter({.tid = 0, .cpu = 0, .value = 1}); @@ -577,6 +618,7 @@ TEST_F(StatCmdSummaryBuilderTest, sort_key_count) { ASSERT_EQ(summaries[1].count, 1); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, sort_key_count_per_thread) { sort_keys_ = {"count_per_thread", "count"}; AddCounter({.tid = 0, .cpu = 0, .value = 1}); @@ -588,6 +630,7 @@ TEST_F(StatCmdSummaryBuilderTest, sort_key_count_per_thread) { ASSERT_EQ(summaries[2].count, 3); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, sort_key_cpu) { sort_keys_ = {"cpu"}; AddCounter({.tid = 0, .cpu = 1, .value = 2}); @@ -597,6 +640,7 @@ TEST_F(StatCmdSummaryBuilderTest, sort_key_cpu) { ASSERT_EQ(summaries[1].cpu, 1); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummaryBuilderTest, sort_key_pid_tid_name) { AddCounter({.tid = 0, .cpu = 0, .value = 1}); AddCounter({.tid = 1, .cpu = 0, .value = 2}); @@ -609,6 +653,7 @@ TEST_F(StatCmdSummaryBuilderTest, sort_key_pid_tid_name) { } } +// @CddTest = 6.1/C-0-2 class StatCmdSummariesTest : public ::testing::Test { protected: void AddSummary(const std::string event_name, pid_t tid, int cpu, uint64_t count, @@ -637,6 +682,7 @@ class StatCmdSummariesTest : public ::testing::Test { std::unique_ptr<CounterSummaries> summaries_; }; +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummariesTest, task_clock_comment) { AddSummary("task-clock", -1, -1, 1e9, 0); AddSummary("task-clock", 0, -1, 2e9, 0); @@ -648,6 +694,7 @@ TEST_F(StatCmdSummariesTest, task_clock_comment) { ASSERT_EQ(*GetComment(3), "3.000000 cpus used"); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummariesTest, cpu_cycles_comment) { AddSummary("cpu-cycles", -1, -1, 100, 100); AddSummary("cpu-cycles", 0, -1, 200, 100); @@ -659,6 +706,7 @@ TEST_F(StatCmdSummariesTest, cpu_cycles_comment) { ASSERT_EQ(*GetComment(3), "3.000000 GHz"); } +// @CddTest = 6.1/C-0-2 TEST_F(StatCmdSummariesTest, rate_comment) { AddSummary("branch-misses", -1, -1, 1e9, 1e9); AddSummary("branch-misses", 0, -1, 1e6, 1e9); diff --git a/simpleperf/cmd_trace_sched_test.cpp b/simpleperf/cmd_trace_sched_test.cpp index 11ba3ba5..a48f97cb 100644 --- a/simpleperf/cmd_trace_sched_test.cpp +++ b/simpleperf/cmd_trace_sched_test.cpp @@ -43,10 +43,12 @@ static std::unique_ptr<Command> TraceSchedCmd() { return CreateCommandInstance("trace-sched"); } +// @CddTest = 6.1/C-0-2 TEST(trace_sched_cmd, smoke) { TEST_IN_ROOT({ ASSERT_TRUE(TraceSchedCmd()->Run({"--duration", "1"})); }); } +// @CddTest = 6.1/C-0-2 TEST(trace_sched_cmd, report_smoke) { CaptureStdout capture; ASSERT_TRUE(capture.Start()); diff --git a/simpleperf/command_test.cpp b/simpleperf/command_test.cpp index 392ec8ac..a8c59655 100644 --- a/simpleperf/command_test.cpp +++ b/simpleperf/command_test.cpp @@ -27,6 +27,7 @@ class MockCommand : public Command { bool Run(const std::vector<std::string>&) override { return true; } }; +// @CddTest = 6.1/C-0-2 TEST(command, CreateCommandInstance) { ASSERT_TRUE(CreateCommandInstance("mock1") == nullptr); RegisterCommand("mock1", [] { return std::unique_ptr<Command>(new MockCommand); }); @@ -35,6 +36,7 @@ TEST(command, CreateCommandInstance) { ASSERT_TRUE(CreateCommandInstance("mock1") == nullptr); } +// @CddTest = 6.1/C-0-2 TEST(command, GetAllCommands) { size_t command_count = GetAllCommandNames().size(); RegisterCommand("mock1", [] { return std::unique_ptr<Command>(new MockCommand); }); @@ -43,6 +45,7 @@ TEST(command, GetAllCommands) { ASSERT_EQ(command_count, GetAllCommandNames().size()); } +// @CddTest = 6.1/C-0-2 TEST(command, GetValueForOption) { MockCommand command; uint64_t value; @@ -70,6 +73,7 @@ TEST(command, GetValueForOption) { ASSERT_DOUBLE_EQ(double_value, 3.2); } +// @CddTest = 6.1/C-0-2 TEST(command, PreprocessOptions) { MockCommand cmd; OptionValueMap options; @@ -156,6 +160,7 @@ TEST(command, PreprocessOptions) { &ordered_options, nullptr)); } +// @CddTest = 6.1/C-0-2 TEST(command, OptionValueMap) { OptionValue value; value.uint_value = 10; diff --git a/simpleperf/doc/collect_etm_data_for_autofdo.md b/simpleperf/doc/collect_etm_data_for_autofdo.md index 059ffb4e..e86f1bba 100644 --- a/simpleperf/doc/collect_etm_data_for_autofdo.md +++ b/simpleperf/doc/collect_etm_data_for_autofdo.md @@ -81,20 +81,15 @@ branch with count info for binary2 We need to split perf_inject.data, and make sure one file only contains info for one binary. -Then we can use [AutoFDO](https://github.com/google/autofdo) to create profile. AutoFDO only works -for binaries having an executable segment as its first loadable segment. But binaries built in -Android may not follow this rule. Simpleperf inject command knows how to work around this problem. -But there is a check in AutoFDO forcing binaries to start with an executable segment. We need to -disable the check in AutoFDO, by commenting out L127-L136 in -https://github.com/google/autofdo/commit/188db2834ce74762ed17108ca344916994640708#diff-2d132ecbb5e4f13e0da65419f6d1759dd27d6b696786dd7096c0c34d499b1710R127-R136. -Then we can use `create_llvm_prof` in AutoFDO to create profiles used by clang. +Then we can use [AutoFDO](https://github.com/google/autofdo) to create profile. Follow README.md +in AutoFDO to build create_llvm_prof, then use `create_llvm_prof` to create profiles for clang. ```sh # perf_inject_binary1.data is split from perf_inject.data, and only contains branch info for binary1. -host $ autofdo/create_llvm_prof -profile perf_inject_binary1.data -profiler text -binary path_of_binary1 -out a.prof -format binary +host $ create_llvm_prof -profile perf_inject_binary1.data -profiler text -binary path_of_binary1 -out a.prof -format binary # perf_inject_kernel.data is split from perf_inject.data, and only contains branch info for [kernel.kallsyms]. -host $ autofdo/create_llvm_prof -profile perf_inject_kernel.data -profiler text -binary vmlinux -out a.prof -format binary +host $ create_llvm_prof -profile perf_inject_kernel.data -profiler text -binary vmlinux -out a.prof -format binary ``` Then we can use a.prof for PGO during compilation, via `-fprofile-sample-use=a.prof`. @@ -121,14 +116,13 @@ Step 2: Run `etm_test_loop` on device, and collect ETM data for its running. (host) <AOSP>$ adb push out/target/product/generic_arm64/system/bin/etm_test_loop /data/local/tmp (host) <AOSP>$ adb root (host) <AOSP>$ adb shell -(device) / # cd /data/local/tmp -(device) /data/local/tmp # chmod a+x etm_test_loop -(device) /data/local/tmp # simpleperf record -e cs-etm:u ./etm_test_loop -simpleperf I cmd_record.cpp:729] Recorded for 0.0370068 seconds. Start post processing. -simpleperf I cmd_record.cpp:799] Aux data traced: 1689136 -(device) /data/local/tmp # simpleperf inject -i perf.data --output branch-list -o branch_list.data -simpleperf W dso.cpp:557] failed to read min virtual address of [vdso]: File not found -(device) /data/local/tmp # exit +(device) / $ cd /data/local/tmp +(device) /data/local/tmp $ chmod a+x etm_test_loop +(device) /data/local/tmp $ simpleperf record -e cs-etm:u ./etm_test_loop +simpleperf I cmd_record.cpp:809] Recorded for 0.033556 seconds. Start post processing. +simpleperf I cmd_record.cpp:879] Aux data traced: 1,134,720 +(device) /data/local/tmp $ simpleperf inject -i perf.data --output branch-list -o branch_list.data +(device) /data/local/tmp $ exit (host) <AOSP>$ adb pull /data/local/tmp/branch_list.data ``` @@ -137,47 +131,43 @@ Step 3: Convert ETM data to AutoFDO data. ```sh # Build simpleperf tool on host. (host) <AOSP>$ make simpleperf_ndk -(host) <AOSP>$ simpleperf_ndk64 inject -i branch_list.data -o perf_inject_etm_test_loop.data --symdir out/target/product/generic_arm64/symbols/system/bin -simpleperf W cmd_inject.cpp:505] failed to build instr ranges for binary [vdso]: File not found +(host) <AOSP>$ simpleperf inject -i branch_list.data -o perf_inject_etm_test_loop.data --symdir out/target/product/generic_arm64/symbols/system/bin (host) <AOSP>$ cat perf_inject_etm_test_loop.data -13 -1000-1010:1 -1014-1050:1 +14 +4000-4010:1 +4014-4048:1 ... -112c->0:1 +418c->0:1 +// build_id: 0xa6fc5b506adf9884cdb680b4893c505a00000000 // /data/local/tmp/etm_test_loop (host) <AOSP>$ create_llvm_prof -profile perf_inject_etm_test_loop.data -profiler text -binary out/target/product/generic_arm64/symbols/system/bin/etm_test_loop -out etm_test_loop.afdo -format binary (host) <AOSP>$ ls -lh etm_test_loop.afdo -rw-r--r-- 1 user group 241 Aug 29 16:04 etm_test_loop.afdo +rw-r--r-- 1 user group 241 Apr 30 09:52 etm_test_loop.afdo ``` Step 4: Use AutoFDO data to build optimized binary. ```sh -(host) <AOSP>$ mkdir toolchain/pgo-profiles/sampling/ (host) <AOSP>$ cp etm_test_loop.afdo toolchain/pgo-profiles/sampling/ (host) <AOSP>$ vi toolchain/pgo-profiles/sampling/Android.bp -# edit Android.bp to add a fdo_profile module -# soong_namespace {} +# Edit Android.bp to add a fdo_profile module: # # fdo_profile { -# name: "etm_test_loop_afdo", -# profile: ["etm_test_loop.afdo"], +# name: "etm_test_loop", +# profile: "etm_test_loop.afdo" # } -``` - -`soong_namespace` is added to support fdo_profile modules with the same name - -In a product config mk file, update `PRODUCT_AFDO_PROFILES` with - -```make -PRODUCT_AFDO_PROFILES += etm_test_loop://toolchain/pgo-profiles/sampling:etm_test_loop_afdo -``` - -```sh +(host) <AOSP>$ vi toolchain/pgo-profiles/sampling/afdo_profiles.mk +# Edit afdo_profiles.mk to add etm_test_loop profile mapping: +# +# AFDO_PROFILES += keystore2://toolchain/pgo-profiles/sampling:keystore2 \ +# ... +# server_configurable_flags://toolchain/pgo-profiles/sampling:server_configurable_flags \ +# etm_test_loop://toolchain/pgo-profiles/sampling:etm_test_loop +# (host) <AOSP>$ vi system/extras/simpleperf/runtest/Android.bp -# edit Android.bp to enable afdo for etm_test_loop. +# Edit Android.bp to enable afdo for etm_test_loop: +# # cc_binary { # name: "etm_test_loop", # srcs: ["etm_test_loop.cpp"], @@ -186,6 +176,14 @@ PRODUCT_AFDO_PROFILES += etm_test_loop://toolchain/pgo-profiles/sampling:etm_tes (host) <AOSP>$ make etm_test_loop ``` +We can check if `etm_test_loop.afdo` is used when building etm_test_loop. + +```sh +(host) <AOSP>$ gzip -d out/verbose.log.gz +(host) <AOSP>$ cat out/verbose.log | grep etm_test_loop.afdo + ... -fprofile-sample-use=toolchain/pgo-profiles/sampling/etm_test_loop.afdo ... +``` + If comparing the disassembly of `out/target/product/generic_arm64/symbols/system/bin/etm_test_loop` before and after optimizing with AutoFDO data, we can see different preferences when branching. diff --git a/simpleperf/doc/scripts_reference.md b/simpleperf/doc/scripts_reference.md index d118ed2c..af74458e 100644 --- a/simpleperf/doc/scripts_reference.md +++ b/simpleperf/doc/scripts_reference.md @@ -151,6 +151,9 @@ $ ./report_html.py --add_disassembly # Adding disassembly for all binaries can cost a lot of time. So we can choose to only add # disassembly for selected binaries. $ ./report_html.py --add_disassembly --binary_filter libgame.so +# Add disassembly and source code for binaries belonging to an app with package name +# com.example.myapp. +$ ./report_html.py --add_source_code --add_disassembly --binary_filter com.example.myapp # report_html.py accepts more than one recording data file. $ ./report_html.py -i perf1.data perf2.data diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp index 607bf7d8..1380ada9 100644 --- a/simpleperf/dso.cpp +++ b/simpleperf/dso.cpp @@ -875,8 +875,8 @@ class KernelModuleDso : public Dso { if (elf) { status = elf->ParseSymbols(symbol_callback); } - ReportReadElfSymbolResult(status, path_, GetDebugFilePath(), - symbols_.empty() ? android::base::WARNING : android::base::DEBUG); + // Don't warn when a kernel module is missing. As a backup, we read symbols from /proc/kallsyms. + ReportReadElfSymbolResult(status, path_, GetDebugFilePath(), android::base::DEBUG); SortAndFixSymbols(symbols); return symbols; } @@ -897,6 +897,10 @@ class KernelModuleDso : public Dso { // and its vaddr_in_file from the kernel module file. Then other symbols in .text section can // be mapped in the same way. Below we use the second method. + if (!IsRegularFile(GetDebugFilePath())) { + return; + } + // 1. Select a module symbol in /proc/kallsyms. kernel_dso_->LoadSymbols(); const auto& kernel_symbols = kernel_dso_->GetSymbols(); diff --git a/simpleperf/dso_test.cpp b/simpleperf/dso_test.cpp index 11bcde19..f9199e41 100644 --- a/simpleperf/dso_test.cpp +++ b/simpleperf/dso_test.cpp @@ -30,6 +30,7 @@ using namespace simpleperf; using namespace simpleperf_dso_impl; +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, use_build_id_list) { // Create a temp symdir with build_id_list. TemporaryDir tmpdir; @@ -57,6 +58,7 @@ static std::string ConvertPathSeparator(const std::string& path) { return result; } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, concatenating_symfs_dir) { DebugElfFileFinder finder; ASSERT_TRUE(finder.SetSymFsDir(GetTestDataDir())); @@ -73,6 +75,7 @@ TEST(DebugElfFileFinder, concatenating_symfs_dir) { GetTestDataDir() + apk_path + "!/" + NATIVELIB_IN_APK); } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, use_vdso) { DebugElfFileFinder finder; std::string fake_vdso32 = "fake_vdso32"; @@ -84,6 +87,7 @@ TEST(DebugElfFileFinder, use_vdso) { ASSERT_EQ(finder.FindDebugFile("[vdso]", true, build_id), fake_vdso64); } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, add_symbol_dir) { DebugElfFileFinder finder; ASSERT_FALSE(finder.AddSymbolDir(GetTestDataDir() + "dir_not_exist")); @@ -94,6 +98,7 @@ TEST(DebugElfFileFinder, add_symbol_dir) { symfs_dir + OS_PATH_SEPARATOR + "elf_for_build_id_check"); } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, build_id_list) { DebugElfFileFinder finder; // Find file in symfs dir with correct build_id_list. @@ -109,6 +114,7 @@ TEST(DebugElfFileFinder, build_id_list) { ASSERT_EQ(finder.FindDebugFile("elf", false, CHECK_ELF_FILE_BUILD_ID), "elf"); } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, no_build_id) { DebugElfFileFinder finder; // If not given a build id, we should match an elf in symfs without build id. @@ -118,6 +124,7 @@ TEST(DebugElfFileFinder, no_build_id) { ASSERT_EQ(finder.FindDebugFile("elf", false, build_id), symfs_dir + OS_PATH_SEPARATOR + "elf"); } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, find_basename_in_symfs_dir) { DebugElfFileFinder finder; // Find normal elf file. @@ -136,6 +143,7 @@ TEST(DebugElfFileFinder, find_basename_in_symfs_dir) { symfs_dir + OS_PATH_SEPARATOR + "elf"); } +// @CddTest = 6.1/C-0-2 TEST(DebugElfFileFinder, build_id_mismatch) { DebugElfFileFinder finder; finder.SetSymFsDir(GetTestDataDir()); @@ -148,6 +156,7 @@ TEST(DebugElfFileFinder, build_id_mismatch) { ASSERT_NE(stderr_output.find("build id mismatch"), std::string::npos); } +// @CddTest = 6.1/C-0-2 TEST(dso, dex_file_dso) { #if defined(__linux__) for (DsoType dso_type : {DSO_DEX_FILE, DSO_ELF_FILE}) { @@ -177,6 +186,7 @@ TEST(dso, dex_file_dso) { #endif // defined(__linux__) } +// @CddTest = 6.1/C-0-2 TEST(dso, dex_file_offsets) { std::unique_ptr<Dso> dso = Dso::CreateDso(DSO_DEX_FILE, ""); ASSERT_TRUE(dso); @@ -186,6 +196,7 @@ TEST(dso, dex_file_offsets) { ASSERT_EQ(*dso->DexFileOffsets(), std::vector<uint64_t>({0x1, 0x2, 0x3, 0x4, 0x5})); } +// @CddTest = 6.1/C-0-2 TEST(dso, embedded_elf) { const std::string file_path = GetUrlInApk(GetTestData(APK_FILE), NATIVELIB_IN_APK); std::unique_ptr<Dso> dso = Dso::CreateDso(DSO_ELF_FILE, file_path); @@ -205,12 +216,14 @@ TEST(dso, embedded_elf) { ASSERT_EQ(build_id, native_lib_build_id); } +// @CddTest = 6.1/C-0-2 TEST(dso, IpToVaddrInFile) { std::unique_ptr<Dso> dso = Dso::CreateDso(DSO_ELF_FILE, GetTestData("libc.so")); ASSERT_TRUE(dso); ASSERT_EQ(0xa5140, dso->IpToVaddrInFile(0xe9201140, 0xe9201000, 0xa5000)); } +// @CddTest = 6.1/C-0-2 TEST(dso, kernel_address_randomization) { // Use ELF_FILE as a fake kernel vmlinux. const std::string vmlinux_path = GetTestData(ELF_FILE); @@ -234,6 +247,7 @@ TEST(dso, kernel_address_randomization) { ASSERT_STREQ(symbol->Name(), "GlobalFunc"); } +// @CddTest = 6.1/C-0-2 TEST(dso, find_vmlinux_in_symdirs) { // Create a symdir. TemporaryDir tmpdir; @@ -260,6 +274,7 @@ TEST(dso, find_vmlinux_in_symdirs) { ASSERT_EQ(0x400927, dso->IpToVaddrInFile(0x800527, 0x800000, 0)); } +// @CddTest = 6.1/C-0-2 TEST(dso, kernel_module) { // Test finding debug files for kernel modules. Dso::SetSymFsDir(GetTestDataDir()); @@ -272,14 +287,16 @@ TEST(dso, kernel_module) { ASSERT_EQ(dso->GetDebugFilePath(), GetTestData(ELF_FILE)); } +// @CddTest = 6.1/C-0-2 TEST(dso, kernel_module_CalculateMinVaddr) { // Create fake Dso objects. auto kernel_dso = Dso::CreateDso(DSO_KERNEL, DEFAULT_KERNEL_MMAP_NAME); ASSERT_TRUE(kernel_dso); const uint64_t module_memory_start = 0xffffffa9bc790000ULL; const uint64_t module_memory_size = 0x8d7000ULL; + TemporaryFile tmpfile; auto module_dso = - Dso::CreateKernelModuleDso("fake_module.ko", module_memory_start, + Dso::CreateKernelModuleDso(tmpfile.path, module_memory_start, module_memory_start + module_memory_size, kernel_dso.get()); ASSERT_TRUE(module_dso); @@ -302,6 +319,7 @@ TEST(dso, kernel_module_CalculateMinVaddr) { ASSERT_EQ(module_dso->IpToVaddrInFile(0xffffffa9bc7a64e8ULL, module_memory_start, 0), 0x144e8); } +// @CddTest = 6.1/C-0-2 TEST(dso, symbol_map_file) { auto dso = Dso::CreateDso(DSO_SYMBOL_MAP_FILE, "perf-123.map"); ASSERT_TRUE(dso); @@ -310,6 +328,7 @@ TEST(dso, symbol_map_file) { ASSERT_EQ(0x12345678, dso->IpToVaddrInFile(0x12345678, 0xe9201000, 0xa5000)); } +// @CddTest = 6.1/C-0-2 TEST(dso, FunctionName) { Symbol symbol = Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x0, 0x1); ASSERT_EQ(symbol.FunctionName(), "ctep.v"); @@ -319,6 +338,7 @@ TEST(dso, FunctionName) { ASSERT_EQ(symbol.FunctionName(), "ctep.v"); } +// @CddTest = 6.1/C-0-2 TEST(dso, search_debug_file_only_when_needed) { Dso::SetBuildIds({std::make_pair("/elf", BuildId("1b12a384a9f4a3f3659b7171ca615dbec3a81f71"))}); Dso::SetSymFsDir(GetTestDataDir()); @@ -330,6 +350,7 @@ TEST(dso, search_debug_file_only_when_needed) { capture.Stop(); } +// @CddTest = 6.1/C-0-2 TEST(dso, read_symbol_warning) { { // Don't warn when the file may not be an ELF file. @@ -370,6 +391,7 @@ TEST(dso, read_symbol_warning) { } } +// @CddTest = 6.1/C-0-2 TEST(dso, demangle) { ASSERT_EQ(Dso::Demangle("main"), "main"); ASSERT_EQ(Dso::Demangle("_ZN4main4main17h2a68d4d833d7495aE"), "main::main::h2a68d4d833d7495a"); diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index 88587dc3..55074d8a 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -75,6 +75,9 @@ std::vector<int> GetOnlineCpus() { static void GetAllModuleFiles(const std::string& path, std::unordered_map<std::string, std::string>* module_file_map) { + if (!IsDir(path)) { + return; + } for (const auto& name : GetEntriesInDir(path)) { std::string entry_path = path + "/" + name; if (IsRegularFile(entry_path) && android::base::EndsWith(name, ".ko")) { @@ -94,9 +97,13 @@ static std::vector<KernelMmap> GetModulesInUse() { } std::unordered_map<std::string, std::string> module_file_map; #if defined(__ANDROID__) - // Search directories listed in "File locations" section in - // https://source.android.com/devices/architecture/kernel/modular-kernels. - for (const auto& path : {"/vendor/lib/modules", "/odm/lib/modules", "/lib/modules"}) { + // On Android, kernel modules are stored in /system/lib/modules, /vendor/lib/modules, + // /odm/lib/modules. + // See https://source.android.com/docs/core/architecture/partitions/gki-partitions and + // https://source.android.com/docs/core/architecture/partitions/vendor-odm-dlkm-partition. + // They can also be stored in vendor_kernel_ramdisk.img, which isn't accessible from userspace. + // See https://source.android.com/docs/core/architecture/kernel/kernel-module-support. + for (const auto& path : {"/system/lib/modules", "/vendor/lib/modules", "/odm/lib/modules"}) { GetAllModuleFiles(path, &module_file_map); } #else diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp index 87c4998a..49a9bb53 100644 --- a/simpleperf/environment_test.cpp +++ b/simpleperf/environment_test.cpp @@ -29,6 +29,7 @@ namespace fs = std::filesystem; using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(environment, PrepareVdsoFile) { std::string content; ASSERT_TRUE(android::base::ReadFileToString("/proc/self/maps", &content)); @@ -46,6 +47,7 @@ TEST(environment, PrepareVdsoFile) { ASSERT_NE(dso->GetDebugFilePath(), "[vdso]"); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetHardwareFromCpuInfo) { std::string cpu_info = "CPU revision : 10\n\n" @@ -54,6 +56,7 @@ TEST(environment, GetHardwareFromCpuInfo) { GetHardwareFromCpuInfo(cpu_info)); } +// @CddTest = 6.1/C-0-2 TEST(environment, MappedFileOnlyExistInMemory) { ASSERT_TRUE(MappedFileOnlyExistInMemory("")); ASSERT_TRUE(MappedFileOnlyExistInMemory("[stack]")); @@ -66,6 +69,7 @@ TEST(environment, MappedFileOnlyExistInMemory) { ASSERT_FALSE(MappedFileOnlyExistInMemory("/system/lib64/libc.so")); } +// @CddTest = 6.1/C-0-2 TEST(environment, SetPerfEventLimits) { #if defined(__ANDROID__) if (GetAndroidVersion() <= kAndroidVersionP) { @@ -101,10 +105,12 @@ TEST(environment, SetPerfEventLimits) { #endif } +// @CddTest = 6.1/C-0-2 TEST(environment, GetKernelVersion) { ASSERT_TRUE(GetKernelVersion()); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetModuleBuildId) { BuildId build_id; fs::path dir(GetTestData("sysfs/module/fake_kernel_module/notes")); @@ -114,6 +120,7 @@ TEST(environment, GetModuleBuildId) { ASSERT_EQ(build_id, BuildId("3e0ba155286f3454")); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetKernelAndModuleMmaps) { TEST_REQUIRE_ROOT(); KernelMmap kernel_mmap; @@ -124,12 +131,14 @@ TEST(environment, GetKernelAndModuleMmaps) { ASSERT_GT(kernel_mmap.start_addr, 0); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetProcessUid) { std::optional<uid_t> uid = GetProcessUid(getpid()); ASSERT_TRUE(uid.has_value()); ASSERT_EQ(uid.value(), getuid()); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetAppType) { TEST_REQUIRE_APPS(); ASSERT_EQ(GetAppType("com.android.simpleperf.debuggable"), "debuggable"); @@ -137,12 +146,14 @@ TEST(environment, GetAppType) { ASSERT_EQ(GetAppType("com.android.simpleperf.app_not_exist"), "not_exist"); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetMemorySize) { auto value = GetMemorySize(); ASSERT_TRUE(value); ASSERT_GT(value.value(), 0); } +// @CddTest = 6.1/C-0-2 TEST(environment, GetARMCpuModels) { #if defined(__aarch64__) && defined(__ANDROID__) auto models = GetARMCpuModels(); diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index c75f8049..1a7cdef8 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -233,6 +233,8 @@ bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_nam // enabling/disabling etm devices. So don't adjust frequency by default. selection->event_attr.freq = 0; selection->event_attr.sample_period = 1; + // An ETM event can't be enabled without mmap aux buffer. So disable it by default. + selection->event_attr.disabled = 1; } else { selection->event_attr.freq = 1; // Set default sample freq here may print msg "Adjust sample freq to max allowed sample @@ -461,6 +463,17 @@ void EventSelectionSet::SetEnableCondition(bool enable_on_open, bool enable_on_e } } +bool EventSelectionSet::IsEnabledOnExec() const { + for (const auto& group : groups_) { + for (const auto& selection : group.selections) { + if (!selection.event_attr.enable_on_exec) { + return false; + } + } + } + return true; +} + void EventSelectionSet::SampleIdAll() { for (auto& group : groups_) { for (auto& selection : group.selections) { @@ -939,4 +952,61 @@ bool EventSelectionSet::SetEnableEvents(bool enable) { return true; } +bool EventSelectionSet::EnableETMEvents() { + for (auto& group : groups_) { + for (auto& sel : group.selections) { + if (!sel.event_type_modifier.event_type.IsEtmEvent()) { + continue; + } + for (auto& fd : sel.event_fds) { + if (!fd->SetEnableEvent(true)) { + return false; + } + } + } + } + return true; +} + +bool EventSelectionSet::DisableETMEvents() { + for (auto& group : groups_) { + for (auto& sel : group.selections) { + if (!sel.event_type_modifier.event_type.IsEtmEvent()) { + continue; + } + // When using ETR, ETM data is flushed to the aux buffer of the last cpu disabling ETM events. + // To avoid overflowing the aux buffer for one cpu, rotate the last cpu disabling ETM events. + if (etm_event_cpus_.empty()) { + for (const auto& fd : sel.event_fds) { + etm_event_cpus_.insert(fd->Cpu()); + } + if (etm_event_cpus_.empty()) { + continue; + } + etm_event_cpus_it_ = etm_event_cpus_.begin(); + } + int last_disabled_cpu = *etm_event_cpus_it_; + if (++etm_event_cpus_it_ == etm_event_cpus_.end()) { + etm_event_cpus_it_ = etm_event_cpus_.begin(); + } + + for (auto& fd : sel.event_fds) { + if (fd->Cpu() != last_disabled_cpu) { + if (!fd->SetEnableEvent(false)) { + return false; + } + } + } + for (auto& fd : sel.event_fds) { + if (fd->Cpu() == last_disabled_cpu) { + if (!fd->SetEnableEvent(false)) { + return false; + } + } + } + } + } + return true; +} + } // namespace simpleperf diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h index e046035b..a892d51e 100644 --- a/simpleperf/event_selection_set.h +++ b/simpleperf/event_selection_set.h @@ -122,6 +122,7 @@ class EventSelectionSet { std::map<int, size_t> GetHardwareCountersForCpus() const; void SetEnableCondition(bool enable_on_open, bool enable_on_exec); + bool IsEnabledOnExec() const; void SampleIdAll(); // Only set sample rate for events that haven't set sample rate. void SetSampleRateForNewEvents(const SampleRate& rate); @@ -179,6 +180,8 @@ class EventSelectionSet { double check_interval_in_sec = DEFAULT_PERIOD_TO_CHECK_MONITORED_TARGETS_IN_SEC); bool SetEnableEvents(bool enable); + bool EnableETMEvents(); + bool DisableETMEvents(); private: struct EventSelection { @@ -232,6 +235,9 @@ class EventSelectionSet { std::optional<SampleRate> sample_rate_; std::optional<std::vector<int>> cpus_; + std::set<int> etm_event_cpus_; + std::set<int>::const_iterator etm_event_cpus_it_; + DISALLOW_COPY_AND_ASSIGN(EventSelectionSet); }; diff --git a/simpleperf/event_selection_set_test.cpp b/simpleperf/event_selection_set_test.cpp index 2cf0e0a6..e60d81a8 100644 --- a/simpleperf/event_selection_set_test.cpp +++ b/simpleperf/event_selection_set_test.cpp @@ -20,6 +20,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(EventSelectionSet, set_sample_rate_for_new_events) { EventSelectionSet event_selection_set(false); ASSERT_TRUE(event_selection_set.AddEventType("cpu-clock:u")); @@ -43,6 +44,7 @@ TEST(EventSelectionSet, set_sample_rate_for_new_events) { ASSERT_EQ(attrs[3].attr.sample_freq, 200); } +// @CddTest = 6.1/C-0-2 TEST(EventSelectionSet, add_event_with_sample_rate) { EventSelectionSet event_selection_set(false); ASSERT_TRUE(event_selection_set.AddEventType("cpu-clock:u")); @@ -57,6 +59,7 @@ TEST(EventSelectionSet, add_event_with_sample_rate) { ASSERT_EQ(attrs[1].attr.sample_period, 1); } +// @CddTest = 6.1/C-0-2 TEST(EventSelectionSet, set_cpus_for_new_events) { EventSelectionSet event_selection_set(false); std::vector<int> online_cpus = GetOnlineCpus(); diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp index 0d10d12d..d8379141 100644 --- a/simpleperf/event_type.cpp +++ b/simpleperf/event_type.cpp @@ -104,7 +104,11 @@ class TracepointStringFinder : public EventTypeFinder { protected: void LoadTypes() override { for (const auto& line : android::base::Split(s_, "\n")) { - std::vector<std::string> items = android::base::Split(line, " "); + std::string str = android::base::Trim(line); + if (str.empty()) { + continue; + } + std::vector<std::string> items = android::base::Split(str, " "); CHECK_EQ(items.size(), 2u); std::string event_name = items[0]; uint64_t id; diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h index 14863caa..d85f71d1 100644 --- a/simpleperf/event_type.h +++ b/simpleperf/event_type.h @@ -55,6 +55,7 @@ struct EventType { bool IsHardwareEvent() const { return type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE || type == PERF_TYPE_RAW; } + bool IsTracepointEvent() const { return type == PERF_TYPE_TRACEPOINT; } std::vector<int> GetPmuCpumask(); diff --git a/simpleperf/kallsyms_test.cpp b/simpleperf/kallsyms_test.cpp index cacd1634..d2bc8583 100644 --- a/simpleperf/kallsyms_test.cpp +++ b/simpleperf/kallsyms_test.cpp @@ -39,6 +39,7 @@ static bool KernelSymbolsMatch(const KernelSymbol& sym1, const KernelSymbol& sym ModulesMatch(sym1.module, sym2.module); } +// @CddTest = 6.1/C-0-2 TEST(kallsyms, ProcessKernelSymbols) { std::string data = "ffffffffa005c4e4 d __warned.41698 [libsas]\n" @@ -64,6 +65,7 @@ TEST(kallsyms, ProcessKernelSymbols) { data, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol))); } +// @CddTest = 6.1/C-0-2 TEST(kallsyms, ProcessKernelSymbols_ignore_arm_mapping_symbols) { std::string data = "aaaaaaaaaaaaaaaa t $x.9 [coresight_etm4x]\n" @@ -84,17 +86,20 @@ TEST(kallsyms, ProcessKernelSymbols_ignore_arm_mapping_symbols) { } #if defined(__ANDROID__) +// @CddTest = 6.1/C-0-2 TEST(kallsyms, GetKernelStartAddress) { TEST_REQUIRE_ROOT(); ASSERT_NE(GetKernelStartAddress(), 0u); } +// @CddTest = 6.1/C-0-2 TEST(kallsyms, LoadKernelSymbols) { TEST_REQUIRE_ROOT(); std::string kallsyms; ASSERT_TRUE(LoadKernelSymbols(&kallsyms)); } +// @CddTest = 6.1/C-0-2 TEST(kallsyms, print_warning) { TEST_REQUIRE_NON_ROOT(); const std::string warning_msg = "Access to kernel symbol addresses is restricted."; diff --git a/simpleperf/perf_regs_test.cpp b/simpleperf/perf_regs_test.cpp index 0af97478..bc33ca9d 100644 --- a/simpleperf/perf_regs_test.cpp +++ b/simpleperf/perf_regs_test.cpp @@ -20,6 +20,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(RegSet, arch) { ArchType arch_pairs[3][2] = { {ARCH_X86_32, ARCH_X86_64}, diff --git a/simpleperf/read_apk_test.cpp b/simpleperf/read_apk_test.cpp index e4dd71f5..c0be177c 100644 --- a/simpleperf/read_apk_test.cpp +++ b/simpleperf/read_apk_test.cpp @@ -22,6 +22,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(read_apk, FindElfInApkByOffset) { ApkInspector inspector; ASSERT_TRUE(inspector.FindElfInApkByOffset("/dev/null", 0) == nullptr); @@ -36,6 +37,7 @@ TEST(read_apk, FindElfInApkByOffset) { ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size()); } +// @CddTest = 6.1/C-0-2 TEST(read_apk, FindElfInApkByName) { ASSERT_TRUE(ApkInspector::FindElfInApkByName("/dev/null", "") == nullptr); ASSERT_TRUE(ApkInspector::FindElfInApkByName(GetTestData(APK_FILE), "") == nullptr); @@ -45,6 +47,7 @@ TEST(read_apk, FindElfInApkByName) { ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size()); } +// @CddTest = 6.1/C-0-2 TEST(read_apk, ParseExtractedInMemoryPath) { std::string zip_path; std::string entry_name; diff --git a/simpleperf/read_dex_file_test.cpp b/simpleperf/read_dex_file_test.cpp index 843a964c..a9860f83 100644 --- a/simpleperf/read_dex_file_test.cpp +++ b/simpleperf/read_dex_file_test.cpp @@ -27,6 +27,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(read_dex_file, smoke) { std::vector<Symbol> symbols; auto symbol_callback = [&](DexFileSymbol* symbol) { diff --git a/simpleperf/read_elf_test.cpp b/simpleperf/read_elf_test.cpp index e2a2cd88..709205a8 100644 --- a/simpleperf/read_elf_test.cpp +++ b/simpleperf/read_elf_test.cpp @@ -32,6 +32,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(read_elf, GetBuildIdFromNoteSection) { BuildId build_id; std::vector<char> data; @@ -62,6 +63,7 @@ TEST(read_elf, GetBuildIdFromNoteSection) { } } +// @CddTest = 6.1/C-0-2 TEST(read_elf, GetBuildIdFromElfFile) { BuildId build_id; ElfStatus status; @@ -71,6 +73,7 @@ TEST(read_elf, GetBuildIdFromElfFile) { ASSERT_EQ(build_id, BuildId(elf_file_build_id)); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, GetBuildIdFromEmbeddedElfFile) { BuildId build_id; ElfStatus status; @@ -103,6 +106,7 @@ void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols) { CheckFunctionSymbols(symbols); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, parse_symbols_from_elf_file_with_correct_build_id) { std::map<std::string, ElfFileSymbol> symbols; ElfStatus status; @@ -113,6 +117,7 @@ TEST(read_elf, parse_symbols_from_elf_file_with_correct_build_id) { CheckElfFileSymbols(symbols); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, parse_symbols_from_elf_file_without_build_id) { std::map<std::string, ElfFileSymbol> symbols; ElfStatus status; @@ -133,6 +138,7 @@ TEST(read_elf, parse_symbols_from_elf_file_without_build_id) { CheckElfFileSymbols(symbols); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, parse_symbols_from_elf_file_with_wrong_build_id) { BuildId build_id("01010101010101010101"); std::map<std::string, ElfFileSymbol> symbols; @@ -141,6 +147,7 @@ TEST(read_elf, parse_symbols_from_elf_file_with_wrong_build_id) { ASSERT_EQ(ElfStatus::BUILD_ID_MISMATCH, status); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, ParseSymbolsFromEmbeddedElfFile) { std::map<std::string, ElfFileSymbol> symbols; ElfStatus status; @@ -152,6 +159,7 @@ TEST(read_elf, ParseSymbolsFromEmbeddedElfFile) { CheckElfFileSymbols(symbols); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, ParseSymbolFromMiniDebugInfoElfFile) { std::map<std::string, ElfFileSymbol> symbols; ElfStatus status; @@ -162,6 +170,7 @@ TEST(read_elf, ParseSymbolFromMiniDebugInfoElfFile) { CheckFunctionSymbols(symbols); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, arm_mapping_symbol) { ASSERT_TRUE(IsArmMappingSymbol("$a")); ASSERT_FALSE(IsArmMappingSymbol("$b")); @@ -169,6 +178,7 @@ TEST(read_elf, arm_mapping_symbol) { ASSERT_FALSE(IsArmMappingSymbol("$a_no_dot")); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, ElfFile_Open) { auto IsValidElfPath = [](const std::string& path) { ElfStatus status; @@ -183,6 +193,7 @@ TEST(read_elf, ElfFile_Open) { ASSERT_EQ(ElfStatus::NO_ERROR, IsValidElfPath(GetTestData(ELF_FILE))); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, check_symbol_for_plt_section) { std::map<std::string, ElfFileSymbol> symbols; ElfStatus status; @@ -193,6 +204,7 @@ TEST(read_elf, check_symbol_for_plt_section) { ASSERT_NE(symbols.find("@plt"), symbols.end()); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, read_elf_with_broken_section_table) { std::string elf_path = GetTestData("libsgmainso-6.4.36.so"); std::map<std::string, ElfFileSymbol> symbols; @@ -211,6 +223,7 @@ TEST(read_elf, read_elf_with_broken_section_table) { ASSERT_EQ(file_offset_of_min_vaddr, 0u); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, ReadMinExecutableVaddr) { ElfStatus status; auto elf = ElfFile::Open(GetTestData("libc.so"), &status); @@ -221,6 +234,7 @@ TEST(read_elf, ReadMinExecutableVaddr) { ASSERT_EQ(file_offset_of_min_vaddr, 0x29000u); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, NoUndefinedSymbol) { // Check if we read undefined symbols (like dlerror) from libc.so. bool has_dlerror = false; @@ -237,6 +251,7 @@ TEST(read_elf, NoUndefinedSymbol) { ASSERT_FALSE(has_dlerror); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, VaddrToOff) { auto elf = ElfFile::Open(GetTestData(ELF_FILE)); ASSERT_TRUE(elf != nullptr); @@ -247,6 +262,7 @@ TEST(read_elf, VaddrToOff) { ASSERT_FALSE(elf->VaddrToOff(0x420000, &off)); } +// @CddTest = 6.1/C-0-2 TEST(read_elf, GetSectionHeader) { auto elf = ElfFile::Open(GetTestData(ELF_FILE)); ASSERT_TRUE(elf != nullptr); diff --git a/simpleperf/read_symbol_map_test.cpp b/simpleperf/read_symbol_map_test.cpp index 9a5ac39b..4365fc9e 100644 --- a/simpleperf/read_symbol_map_test.cpp +++ b/simpleperf/read_symbol_map_test.cpp @@ -25,6 +25,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(read_symbol_map, smoke) { std::string content( "\n" // skip @@ -62,6 +63,7 @@ TEST(read_symbol_map, smoke) { ASSERT_STREQ("six six", symbols[3].Name()); } +// @CddTest = 6.1/C-0-2 TEST(read_symbol_map, v8_basic_perf_prof) { // Interesting sample of jitted function names generated by V8 running the // JetStream2 benchmark. diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp index e43e4bdf..7967362e 100644 --- a/simpleperf/record_file_test.cpp +++ b/simpleperf/record_file_test.cpp @@ -35,6 +35,7 @@ using namespace simpleperf; using namespace simpleperf::PerfFileFormat; +// @CddTest = 6.1/C-0-2 class RecordFileTest : public ::testing::Test { protected: void SetUp() override { close(tmpfile_.release()); } @@ -54,6 +55,7 @@ class RecordFileTest : public ::testing::Test { EventAttrIds attr_ids_; }; +// @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, smoke) { // Write to a record file. std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path); @@ -102,6 +104,7 @@ TEST_F(RecordFileTest, smoke) { ASSERT_TRUE(reader->Close()); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, record_more_than_one_attr) { // Write to a record file. std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path); @@ -126,6 +129,7 @@ TEST_F(RecordFileTest, record_more_than_one_attr) { } } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, write_meta_info_feature_section) { // Write to a record file. std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path); @@ -150,6 +154,7 @@ TEST_F(RecordFileTest, write_meta_info_feature_section) { ASSERT_EQ(reader->GetMetaInfoFeature(), info_map); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, write_debug_unwind_feature_section) { // Write to a record file. std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path); @@ -180,6 +185,7 @@ TEST_F(RecordFileTest, write_debug_unwind_feature_section) { } } +// @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, write_file2_feature_section) { // Write to a record file. std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path); @@ -254,4 +260,4 @@ TEST_F(RecordFileTest, write_file2_feature_section) { } ASSERT_FALSE(error); ASSERT_EQ(file_id, files.size()); -}
\ No newline at end of file +} diff --git a/simpleperf/record_lib_test.cpp b/simpleperf/record_lib_test.cpp index 4c1b7e87..15aa9267 100644 --- a/simpleperf/record_lib_test.cpp +++ b/simpleperf/record_lib_test.cpp @@ -22,6 +22,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(get_all_events, smoke) { std::vector<std::string> events = GetAllEvents(); ASSERT_GT(events.size(), 0u); @@ -36,6 +37,7 @@ static void DoSomeWork() { } } +// @CddTest = 6.1/C-0-2 TEST(counter, add_event) { std::unique_ptr<PerfEventSet> perf( PerfEventSet::CreateInstance(PerfEventSet::Type::kPerfForCounting)); @@ -61,6 +63,7 @@ TEST(counter, add_event) { } } +// @CddTest = 6.1/C-0-2 TEST(counter, different_targets) { auto test_function = [](std::function<void(PerfEventSet*)> set_target_func) { std::unique_ptr<PerfEventSet> perf( @@ -87,6 +90,7 @@ TEST(counter, different_targets) { [](PerfEventSet* perf) { ASSERT_TRUE(perf->MonitorThreadsInCurrentProcess({getpid()})); }); } +// @CddTest = 6.1/C-0-2 TEST(counter, start_stop_multiple_times) { const size_t TEST_COUNT = 10; std::unique_ptr<PerfEventSet> perf( @@ -116,6 +120,7 @@ TEST(counter, start_stop_multiple_times) { } } +// @CddTest = 6.1/C-0-2 TEST(counter, no_change_after_stop) { std::unique_ptr<PerfEventSet> perf( PerfEventSet::CreateInstance(PerfEventSet::Type::kPerfForCounting)); diff --git a/simpleperf/record_test.cpp b/simpleperf/record_test.cpp index bca0b7ff..ed437501 100644 --- a/simpleperf/record_test.cpp +++ b/simpleperf/record_test.cpp @@ -23,6 +23,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 class RecordTest : public ::testing::Test { protected: virtual void SetUp() { @@ -42,16 +43,19 @@ class RecordTest : public ::testing::Test { perf_event_attr event_attr; }; +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, MmapRecordMatchBinary) { MmapRecord record(event_attr, true, 1, 2, 0x1000, 0x2000, 0x3000, "MmapRecord", 0); CheckRecordMatchBinary(record); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, CommRecordMatchBinary) { CommRecord record(event_attr, 1, 2, "CommRecord", 0, 7); CheckRecordMatchBinary(record); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, SampleRecordMatchBinary) { event_attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_CALLCHAIN; @@ -59,6 +63,7 @@ TEST_F(RecordTest, SampleRecordMatchBinary) { CheckRecordMatchBinary(record); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, SampleRecord_exclude_kernel_callchain) { SampleRecord r(event_attr, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, 0); ASSERT_TRUE(r.ExcludeKernelCallChain()); @@ -103,6 +108,7 @@ TEST_F(RecordTest, SampleRecord_exclude_kernel_callchain) { {}, 0)); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, SampleRecord_ReplaceRegAndStackWithCallChain) { event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN; std::vector<std::vector<uint64_t>> user_ip_tests = { @@ -130,6 +136,7 @@ TEST_F(RecordTest, SampleRecord_ReplaceRegAndStackWithCallChain) { } } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, SampleRecord_UpdateUserCallChain) { event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER; SampleRecord r(event_attr, 0, 1, 2, 3, 4, 5, 6, {}, {1, PERF_CONTEXT_USER, 2}, {}, 0); @@ -140,6 +147,7 @@ TEST_F(RecordTest, SampleRecord_UpdateUserCallChain) { CheckRecordEqual(r, expected); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, SampleRecord_AdjustCallChainGeneratedByKernel) { event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER; SampleRecord r(event_attr, 0, 1, 2, 3, 4, 5, 6, {}, {1, 5, 0, PERF_CONTEXT_USER, 6, 0}, {}, 0); @@ -154,6 +162,7 @@ TEST_F(RecordTest, SampleRecord_AdjustCallChainGeneratedByKernel) { CheckRecordEqual(r, expected); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, SampleRecord_PerfSampleReadData) { event_attr.sample_type |= PERF_SAMPLE_READ; event_attr.read_format = @@ -180,6 +189,7 @@ TEST_F(RecordTest, SampleRecord_PerfSampleReadData) { CheckRecordMatchBinary(r2); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, CommRecord) { CommRecord r(event_attr, 1, 2, "init_name", 3, 4); size_t record_size = r.size(); @@ -194,6 +204,7 @@ TEST_F(RecordTest, CommRecord) { CheckRecordMatchBinary(r); } +// @CddTest = 6.1/C-0-2 TEST_F(RecordTest, DebugRecord) { DebugRecord r(1234, "hello"); ASSERT_EQ(r.size() % sizeof(uint64_t), 0); diff --git a/simpleperf/report_utils_test.cpp b/simpleperf/report_utils_test.cpp index ad4b9df3..0d96d7d1 100644 --- a/simpleperf/report_utils_test.cpp +++ b/simpleperf/report_utils_test.cpp @@ -27,6 +27,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(ProguardMappingRetrace, smoke) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -151,6 +152,7 @@ class CallChainReportBuilderTest : public testing::Test { }; }; +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, default_option) { // Test default option: remove_art_frame = true, convert_jit_frame = true. // The callchain shouldn't include interpreter frames. And the JIT frame is @@ -170,6 +172,7 @@ TEST_F(CallChainReportBuilderTest, default_option) { ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, not_convert_jit_frame) { // Test option: remove_art_frame = true, convert_jit_frame = false. // The callchain shouldn't include interpreter frames. And the JIT frame isn't @@ -190,6 +193,7 @@ TEST_F(CallChainReportBuilderTest, not_convert_jit_frame) { ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, not_remove_art_frame) { // Test option: remove_art_frame = false, convert_jit_frame = true. // The callchain should include interpreter frames. And the JIT frame is @@ -222,6 +226,7 @@ TEST_F(CallChainReportBuilderTest, not_remove_art_frame) { ASSERT_EQ(entries[5].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, remove_jit_frame_called_by_dex_frame) { // Test option: remove_art_frame = true, convert_jit_frame = true. // The callchain should remove the JIT frame called by a dex frame having the same symbol name. @@ -242,6 +247,7 @@ TEST_F(CallChainReportBuilderTest, remove_jit_frame_called_by_dex_frame) { ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, remove_art_frame_only_near_jvm_method) { // Test option: remove_art_frame = true, convert_jit_frame = true. // The callchain should not remove ART symbols not near a JVM method. @@ -277,6 +283,7 @@ TEST_F(CallChainReportBuilderTest, remove_art_frame_only_near_jvm_method) { ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, keep_art_jni_method) { // Test option: remove_art_frame = true. // The callchain should remove art_jni_trampoline, but keep jni methods. @@ -305,6 +312,7 @@ TEST_F(CallChainReportBuilderTest, keep_art_jni_method) { ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file) { std::vector<uint64_t> fake_ips = { 0x2200, // 2200, // obfuscated_class.obfuscated_java_method @@ -364,6 +372,7 @@ TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file) { ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, not_remove_synthesized_frame_by_default) { std::vector<uint64_t> fake_ips = { 0x2200, // 2200, // obfuscated_class.obfuscated_java_method @@ -400,6 +409,7 @@ TEST_F(CallChainReportBuilderTest, not_remove_synthesized_frame_by_default) { ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, remove_synthesized_frame_with_env_variable) { // Windows doesn't support setenv and unsetenv. So don't test on it. #if !defined(__WIN32) @@ -435,6 +445,7 @@ TEST_F(CallChainReportBuilderTest, remove_synthesized_frame_with_env_variable) { #endif // !defined(__WIN32) } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file_for_jit_method_with_signature) { std::vector<uint64_t> fake_ips = { 0x3200, // 3200, // void ctep.v(cteo, ctgc, ctbn) @@ -460,6 +471,7 @@ TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file_for_jit_method_with ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file_for_compiled_java_method_with_signature) { TemporaryFile tmpfile; @@ -492,6 +504,7 @@ TEST_F(CallChainReportBuilderTest, } } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, convert_jit_frame_for_jit_method_with_signature) { std::vector<uint64_t> fake_ips = { 0x2200, // 2200, // ctep.v @@ -540,6 +553,7 @@ TEST_F(CallChainReportBuilderTest, convert_jit_frame_for_jit_method_with_signatu ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +// @CddTest = 6.1/C-0-2 TEST_F(CallChainReportBuilderTest, remove_method_name) { // Test excluding method names. CallChainReportBuilder builder(thread_tree); @@ -588,6 +602,7 @@ class ThreadReportBuilderTest : public testing::Test { ThreadTree thread_tree; }; +// @CddTest = 6.1/C-0-2 TEST_F(ThreadReportBuilderTest, no_setting) { ThreadReportBuilder builder; ThreadEntry* thread = thread_tree.FindThread(1); @@ -595,6 +610,7 @@ TEST_F(ThreadReportBuilderTest, no_setting) { ASSERT_TRUE(IsReportEqual(report, ThreadReport(1, 1, "thread1"))); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadReportBuilderTest, aggregate_threads) { ThreadReportBuilder builder; ASSERT_TRUE(builder.AggregateThreads({"thread-pool.*"})); @@ -609,6 +625,7 @@ TEST_F(ThreadReportBuilderTest, aggregate_threads) { ASSERT_TRUE(IsReportEqual(report, ThreadReport(1, 2, "thread-pool.*"))); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadReportBuilderTest, aggregate_threads_bad_regex) { ThreadReportBuilder builder; ASSERT_FALSE(builder.AggregateThreads({"?thread-pool*"})); diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp index bee187d7..e2df1cec 100644 --- a/simpleperf/sample_tree_test.cpp +++ b/simpleperf/sample_tree_test.cpp @@ -132,6 +132,7 @@ class SampleTreeTest : public testing::Test { std::unique_ptr<TestSampleTreeBuilder> sample_tree_builder; }; +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, ip_in_map) { sample_tree_builder->AddSample(1, 1, 1, false); sample_tree_builder->AddSample(1, 1, 2, false); @@ -142,6 +143,7 @@ TEST_F(SampleTreeTest, ip_in_map) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, different_pid) { sample_tree_builder->AddSample(1, 1, 1, false); sample_tree_builder->AddSample(2, 2, 1, false); @@ -152,6 +154,7 @@ TEST_F(SampleTreeTest, different_pid) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, different_tid) { sample_tree_builder->AddSample(1, 1, 1, false); sample_tree_builder->AddSample(1, 11, 1, false); @@ -162,6 +165,7 @@ TEST_F(SampleTreeTest, different_tid) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, different_comm) { sample_tree_builder->AddSample(1, 1, 1, false); thread_tree.SetThreadName(1, 1, "p1t1_comm2"); @@ -173,6 +177,7 @@ TEST_F(SampleTreeTest, different_comm) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, different_map) { sample_tree_builder->AddSample(1, 1, 1, false); sample_tree_builder->AddSample(1, 1, 6, false); @@ -183,6 +188,7 @@ TEST_F(SampleTreeTest, different_map) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, unmapped_sample) { sample_tree_builder->AddSample(1, 1, 0, false); sample_tree_builder->AddSample(1, 1, 31, false); @@ -194,6 +200,7 @@ TEST_F(SampleTreeTest, unmapped_sample) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST_F(SampleTreeTest, map_kernel) { sample_tree_builder->AddSample(1, 1, 10, true); sample_tree_builder->AddSample(1, 1, 10, false); @@ -204,6 +211,7 @@ TEST_F(SampleTreeTest, map_kernel) { CheckSamples(expected_samples); } +// @CddTest = 6.1/C-0-2 TEST(sample_tree, overlapped_map) { ThreadTree thread_tree; TestSampleTreeBuilder sample_tree_builder(&thread_tree); @@ -226,6 +234,7 @@ TEST(sample_tree, overlapped_map) { CheckSamples(sample_tree_builder.GetSamples(), expected_samples); } +// @CddTest = 6.1/C-0-2 TEST(thread_tree, symbol_ULLONG_MAX) { ThreadTree thread_tree; thread_tree.ShowIpForUnknownSymbol(); diff --git a/simpleperf/scripts/report_html.js b/simpleperf/scripts/report_html.js index 94e8ae5c..a3f899dd 100644 --- a/simpleperf/scripts/report_html.js +++ b/simpleperf/scripts/report_html.js @@ -379,10 +379,10 @@ class ChartView { }; if (isClockEvent(this.eventInfo)) { this.getSampleWeight = function (eventCount) { - return (eventCount / 1000000.0).toFixed(3) + ' ms'; + return (eventCount / 1000000.0).toFixed(3).toLocaleString() + ' ms'; }; } else { - this.getSampleWeight = (eventCount) => '' + eventCount; + this.getSampleWeight = (eventCount) => eventCount.toLocaleString(); } } @@ -622,10 +622,10 @@ class SampleTableWeightSelectorView { return (eventCount) => (eventCount * 100.0 / this.eventCount).toFixed(2) + '%'; } if (this.curOption == 'event_count') { - return (eventCount) => '' + eventCount; + return (eventCount) => eventCount.toLocaleString(); } if (this.curOption == 'event_count_in_ms') { - return (eventCount) => (eventCount / 1000000.0).toFixed(3); + return (eventCount) => (eventCount / 1000000.0).toFixed(3).toLocaleString(); } } @@ -706,7 +706,7 @@ class SampleTableView { data: data, responsive: true, columnDefs: [ - { orderSequence: [ 'desc' ], targets: [0, 1, 2] }, + { orderSequence: [ 'desc' ], className: 'textRight', targets: [0, 1, 2] }, ], }); dataTable.column(7).visible(false); @@ -1082,13 +1082,13 @@ class SampleWeightSelectorView { } if (this.curOption == 'event_count') { return function(eventCount, _) { - return '' + eventCount; + return eventCount.toLocaleString(); }; } if (this.curOption == 'event_count_in_ms') { return function(eventCount, _) { let timeInMs = eventCount / 1000000.0; - return timeInMs.toFixed(3) + ' ms'; + return timeInMs.toFixed(3).toLocaleString() + ' ms'; }; } } @@ -1590,12 +1590,9 @@ class SourceCodeView { data.addColumn('string', 'Self'); data.addColumn('string', 'Code'); data.addRows(rows); - for (let i = 0; i < rows.length; ++i) { - data.setProperty(i, 0, 'className', 'colForLine'); - for (let j = 1; j <= 2; ++j) { - data.setProperty(i, j, 'className', 'colForCount'); - } - } + data.setColumnProperty(0, 'className', 'colForLine'); + data.setColumnProperty(1, 'className', 'colForCount'); + data.setColumnProperty(2, 'className', 'colForCount'); this.div.append(getHtml('pre', {text: sourceFile.path})); let wrapperDiv = $('<div>'); wrapperDiv.appendTo(this.div); @@ -1687,11 +1684,8 @@ class DisassemblyView { data.addColumn('string', 'Self'); data.addColumn('string', 'Code'); data.addRows(rows); - for (let i = 0; i < rows.length; ++i) { - for (let j = 0; j < 2; ++j) { - data.setProperty(i, j, 'className', 'colForCount'); - } - } + data.setColumnProperty(0, 'className', 'colForCount'); + data.setColumnProperty(1, 'className', 'colForCount'); let wrapperDiv = $('<div>'); wrapperDiv.appendTo(this.div); let table = new google.visualization.Table(wrapperDiv.get(0)); diff --git a/simpleperf/scripts/report_html.py b/simpleperf/scripts/report_html.py index ba143fd0..314a33fe 100755 --- a/simpleperf/scripts/report_html.py +++ b/simpleperf/scripts/report_html.py @@ -982,10 +982,11 @@ class ReportGenerator(object): self.hw.open_tag('script').add( "google.charts.load('current', {'packages': ['corechart', 'table']});").close_tag() self.hw.open_tag('style', type='text/css').add(""" - .colForLine { width: 50px; } - .colForCount { width: 100px; } + .colForLine { width: 50px; text-align: right; } + .colForCount { width: 100px; text-align: right; } .tableCell { font-size: 17px; } .boldTableCell { font-weight: bold; font-size: 17px; } + .textRight { text-align: right; } """).close_tag() self.hw.close_tag('head') self.hw.open_tag('body') @@ -1027,7 +1028,10 @@ def get_args() -> argparse.Namespace: 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.""") + only for selected binaries, whose recorded paths contains [BINARY_FILTER] as + a substring. Example: to select binaries belonging to an app with package + name 'com.example.myapp', use `--binary_filter com.example.myapp`. + """) parser.add_argument( '-j', '--jobs', type=int, default=os.cpu_count(), help='Use multithreading to speed up disassembly and source code annotation.') diff --git a/simpleperf/scripts/simpleperf_utils.py b/simpleperf/scripts/simpleperf_utils.py index 90f94cf7..e536b1b5 100644 --- a/simpleperf/scripts/simpleperf_utils.py +++ b/simpleperf/scripts/simpleperf_utils.py @@ -844,6 +844,7 @@ class Objdump(object): real_path] if arch == 'arm' and 'llvm-objdump' in objdump_path: args += ['--print-imm-hex'] + logging.debug('disassembling: %s', ' '.join(args)) try: subproc = subprocess.Popen(args, stdout=subprocess.PIPE) (stdoutdata, _) = subproc.communicate() diff --git a/simpleperf/test_util.cpp b/simpleperf/test_util.cpp index 7e99b5d5..42dee703 100644 --- a/simpleperf/test_util.cpp +++ b/simpleperf/test_util.cpp @@ -117,18 +117,23 @@ static bool HasNonZeroInstructionEventCount() { return false; } +bool IsInEmulator() { + std::string fingerprint = android::base::GetProperty("ro.system.build.fingerprint", ""); + return android::base::StartsWith(fingerprint, "google/sdk_gphone") || + android::base::StartsWith(fingerprint, "google/sdk_gpc") || + android::base::StartsWith(fingerprint, "generic/cf") || + android::base::StartsWith(fingerprint, "generic/aosp_cf"); +} + bool HasHardwareCounter() { static int has_hw_counter = -1; if (has_hw_counter == -1) { has_hw_counter = 1; auto arch = GetTargetArch(); - std::string fingerprint = android::base::GetProperty("ro.system.build.fingerprint", ""); - bool is_emulator = android::base::StartsWith(fingerprint, "google/sdk_gphone") || - android::base::StartsWith(fingerprint, "google/sdk_gpc") || - android::base::StartsWith(fingerprint, "generic/cf"); + bool in_native_abi = IsInNativeAbi() == std::optional(true); - if (arch == ARCH_X86_64 || arch == ARCH_X86_32 || !in_native_abi || is_emulator) { + if (arch == ARCH_X86_64 || arch == ARCH_X86_32 || !in_native_abi || IsInEmulator()) { // On x86 and x86_64, or when we are not in native abi, it's likely to run on an emulator or // vm without hardware perf counters. It's hard to enumerate them all. So check the support // at runtime. @@ -171,7 +176,12 @@ bool HasPmuCounter() { bool HasTracepointEvents() { static int has_tracepoint_events = -1; if (has_tracepoint_events == -1) { - has_tracepoint_events = (GetTraceFsDir() != nullptr) ? 1 : 0; + has_tracepoint_events = 0; + if (const char* dir = GetTraceFsDir(); dir != nullptr) { + if (IsDir(std::string(dir) + "/events/sched/sched_switch")) { + has_tracepoint_events = 1; + } + } } return has_tracepoint_events == 1; } diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h index 16483103..4264c9a3 100644 --- a/simpleperf/test_util.h +++ b/simpleperf/test_util.h @@ -201,3 +201,5 @@ class AppHelper { std::vector<std::string> installed_packages_; std::unique_ptr<Workload> app_start_proc_; }; + +bool IsInEmulator(); diff --git a/simpleperf/thread_tree_test.cpp b/simpleperf/thread_tree_test.cpp index f5b71ec0..7f1768e8 100644 --- a/simpleperf/thread_tree_test.cpp +++ b/simpleperf/thread_tree_test.cpp @@ -22,6 +22,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 class ThreadTreeTest : public ::testing::Test { protected: void AddMap(uint64_t start, uint64_t end, const std::string& name) { @@ -76,6 +77,7 @@ class ThreadTreeTest : public ::testing::Test { ThreadTree thread_tree_; }; +// @CddTest = 6.1/C-0-2 TEST_F(ThreadTreeTest, maps_smoke) { AddMap(0, 5, "0"); AddMap(10, 15, "1"); @@ -100,6 +102,7 @@ TEST_F(ThreadTreeTest, maps_smoke) { CheckMaps(); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadTreeTest, jit_maps_before_fork) { // Maps for JIT symfiles can arrive before fork records. thread_tree_.AddThreadMap(0, 0, 0, 1, 0, "0", map_flags::PROT_JIT_SYMFILE_MAP); @@ -114,6 +117,7 @@ TEST_F(ThreadTreeTest, jit_maps_before_fork) { ASSERT_EQ(map->flags, map_flags::PROT_JIT_SYMFILE_MAP); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadTreeTest, reused_tid) { // Process 1 has thread 1 and 2. thread_tree_.ForkThread(1, 2, 1, 1); @@ -123,12 +127,14 @@ TEST_F(ThreadTreeTest, reused_tid) { thread_tree_.ForkThread(2, 2, 1, 1); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadTreeTest, reused_tid_without_thread_exit) { // Similar to the above test, but the thread exit record is missing. thread_tree_.ForkThread(1, 2, 1, 1); thread_tree_.ForkThread(2, 2, 1, 1); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadTreeTest, add_symbols_for_process) { std::string symbol_map( "0x2000 0x20 two\n" @@ -144,6 +150,7 @@ TEST_F(ThreadTreeTest, add_symbols_for_process) { ASSERT_STREQ("three", FindSymbol(1, 1, 0x302f)->Name()); } +// @CddTest = 6.1/C-0-2 TEST_F(ThreadTreeTest, invalid_fork) { // tid == ptid ASSERT_FALSE(thread_tree_.ForkThread(1, 2, 1, 2)); diff --git a/simpleperf/tracing_test.cpp b/simpleperf/tracing_test.cpp index 4e4558b8..f4cec740 100644 --- a/simpleperf/tracing_test.cpp +++ b/simpleperf/tracing_test.cpp @@ -32,6 +32,7 @@ static void CheckAdjustFilter(const std::string& filter, bool use_quote, ASSERT_EQ(android::base::Join(used_fields, ","), used_field_str); } +// @CddTest = 6.1/C-0-2 TEST(tracing, adjust_tracepoint_filter) { std::string filter = "((sig >= 1 && sig < 20) || sig == 32) && comm != \"bash\""; CheckAdjustFilter(filter, true, filter, "comm,sig"); @@ -67,6 +68,7 @@ std::ostream& operator<<(std::ostream& os, const TracingField& field) { } } // namespace simpleperf +// @CddTest = 6.1/C-0-2 TEST(tracing, ParseTracingFormat) { std::string data = "name: sched_wakeup_new\n" diff --git a/simpleperf/utils_test.cpp b/simpleperf/utils_test.cpp index 15605338..747c020f 100644 --- a/simpleperf/utils_test.cpp +++ b/simpleperf/utils_test.cpp @@ -25,6 +25,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(utils, ConvertBytesToValue) { char buf[8]; for (int i = 0; i < 8; ++i) { @@ -36,6 +37,7 @@ TEST(utils, ConvertBytesToValue) { ASSERT_EQ(0x0706050403020100ULL, ConvertBytesToValue(buf, 8)); } +// @CddTest = 6.1/C-0-2 TEST(utils, ArchiveHelper) { std::unique_ptr<ArchiveHelper> ahelper = ArchiveHelper::CreateInstance(GetTestData(APK_FILE)); ASSERT_TRUE(ahelper); @@ -61,6 +63,7 @@ TEST(utils, ArchiveHelper) { ASSERT_FALSE(ArchiveHelper::CreateInstance("/dev/zero")); } +// @CddTest = 6.1/C-0-2 TEST(utils, GetCpusFromString) { ASSERT_EQ(GetCpusFromString("0-2"), std::make_optional<std::set<int>>({0, 1, 2})); ASSERT_EQ(GetCpusFromString("0,2-3"), std::make_optional<std::set<int>>({0, 2, 3})); @@ -72,11 +75,13 @@ TEST(utils, GetCpusFromString) { ASSERT_EQ(GetCpusFromString("3,2-1"), std::nullopt); } +// @CddTest = 6.1/C-0-2 TEST(utils, GetTidsFromString) { ASSERT_EQ(GetTidsFromString("0,12,9", false), std::make_optional(std::set<pid_t>({0, 9, 12}))); ASSERT_EQ(GetTidsFromString("-2", false), std::nullopt); } +// @CddTest = 6.1/C-0-2 TEST(utils, GetPidsFromStrings) { ASSERT_EQ(GetPidsFromStrings({"0,12", "9"}, false, false), std::make_optional(std::set<pid_t>({0, 9, 12}))); @@ -91,6 +96,7 @@ TEST(utils, GetPidsFromStrings) { #endif // defined(__linux__) } +// @CddTest = 6.1/C-0-2 TEST(utils, LineReader) { TemporaryFile tmpfile; close(tmpfile.release()); @@ -106,6 +112,7 @@ TEST(utils, LineReader) { ASSERT_TRUE(reader.ReadLine() == nullptr); } +// @CddTest = 6.1/C-0-2 TEST(utils, ReadableCount) { ASSERT_EQ(ReadableCount(0), "0"); ASSERT_EQ(ReadableCount(204), "204"); diff --git a/simpleperf/workload_test.cpp b/simpleperf/workload_test.cpp index f99ec753..e1ac147b 100644 --- a/simpleperf/workload_test.cpp +++ b/simpleperf/workload_test.cpp @@ -24,6 +24,7 @@ using namespace simpleperf; +// @CddTest = 6.1/C-0-2 TEST(workload, success) { IOEventLoop loop; ASSERT_TRUE(loop.AddSignalEvent(SIGCHLD, [&]() { return loop.ExitLoop(); })); @@ -34,6 +35,7 @@ TEST(workload, success) { ASSERT_TRUE(loop.RunLoop()); } +// @CddTest = 6.1/C-0-2 TEST(workload, execvp_failure) { auto workload = Workload::CreateWorkload({"/dev/null"}); ASSERT_TRUE(workload != nullptr); @@ -54,6 +56,7 @@ static void run_signaled_workload() { exit(0); } +// @CddTest = 6.1/C-0-2 TEST(workload, signaled_warning) { ASSERT_EXIT(run_signaled_workload(), testing::ExitedWithCode(0), "child process was terminated by signal"); @@ -72,6 +75,7 @@ static void run_exit_nonzero_workload() { exit(0); } +// @CddTest = 6.1/C-0-2 TEST(workload, exit_nonzero_warning) { ASSERT_EXIT(run_exit_nonzero_workload(), testing::ExitedWithCode(0), "child process exited with exit code"); diff --git a/tools/check_elf_alignment.sh b/tools/check_elf_alignment.sh new file mode 100755 index 00000000..b74f34ae --- /dev/null +++ b/tools/check_elf_alignment.sh @@ -0,0 +1,90 @@ +#!/bin/bash +progname="${0##*/}" +progname="${progname%.sh}" + +# usage: check_elf_alignment.sh [path to *.so files|path to *.apk] + +cleanup_trap() { + if [ -n "${tmp}" -a -d "${tmp}" ]; then + rm -rf ${tmp} + fi + exit $1 +} + +usage() { + echo "Host side script to check the ELF alignment of shared libraries." + echo "Shared libraries are reported ALIGNED when their ELF regions are" + echo "16 KB or 64 KB aligned. Otherwise they are reported as UNALIGNED." + echo + echo "Usage: ${progname} [input-path|input-APK]" +} + +if [ ${#} -ne 1 ]; then + usage + exit +fi + +case ${1} in + --help | -h | -\?) + usage + exit + ;; + + *) + dir="${1}" + ;; +esac + +if ! [ -f "${dir}" -o -d "${dir}" ]; then + echo "Invalid file: ${dir}" >&2 + exit 1 +fi + +if [[ "${dir}" == *.apk ]]; then + trap 'cleanup_trap' EXIT + + if { zipalign --help 2>&1 | grep -q "\-P <pagesize_kb>"; }; then + echo "=== APK zip-alignment ===" + zipalign -v -c -P 16 4 "${dir}" | egrep 'lib/arm64-v8a|lib/x86_64|Verification' + echo "=========================" + else + echo "NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher." + echo " You can install the latest build-tools by running the below command" + echo " and updating your \$PATH:" + echo + echo " sdkmanager \"build-tools;35.0.0-rc3\"" + fi + + dir_filename=$(basename "${dir}") + tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX") + unzip "${dir}" lib/* -d "${tmp}" >/dev/null 2>&1 + dir="${tmp}" +fi + +RED="\e[31m" +GREEN="\e[32m" +ENDCOLOR="\e[0m" + +unaligned_libs=() + +echo +echo "=== ELF alignment ===" + +matches="$(find "${dir}" -name "*.so" -type f)" +IFS=$'\n' +for match in $matches; do + res="$(objdump -p ${match} | grep LOAD | awk '{ print $NF }' | head -1)" + if [[ $res =~ "2**14" ]] || [[ $res =~ "2**16" ]]; then + echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)" + else + echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)" + unaligned_libs+=("${match}") + fi +done + +if [ ${#unaligned_libs[@]} -gt 0 ]; then + echo -e "${RED}Found ${#unaligned_libs[@]} unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).${ENDCOLOR}" +elif [ -n "${dir_filename}" ]; then + echo -e "ELF Verification Successful" +fi +echo "=====================" |