diff --git a/devtools/etdump/etdump_filter.cpp b/devtools/etdump/etdump_filter.cpp new file mode 100644 index 00000000000..91408e24faf --- /dev/null +++ b/devtools/etdump/etdump_filter.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include + +using ::executorch::runtime::DelegateDebugIntId; +using ::executorch::runtime::Error; +using ::executorch::runtime::kUnsetDelegateDebugIntId; + +namespace executorch { +namespace etdump { + +ETDumpFilter::ETDumpFilter() = default; + +Result ETDumpFilter::add_regex(string_view pattern) { + auto regex = std::make_unique(pattern.data()); + if (!regex->ok()) { + return Error::InvalidArgument; // Error during regex compilation + } + regex_patterns_.emplace_back(std::move(regex)); + return true; +} + +Result ETDumpFilter::set_debug_handle_range(size_t start, size_t end) { + if (start >= end) { + return Error::InvalidArgument; // Start is greater than end + } + if (start < 0 || end < 0) { + return Error::InvalidArgument; // Start or end is negative + } + range_start_ = start; + range_end_ = end; + return true; +} + +Result ETDumpFilter::filter_name_(const char* name) { + if (name == nullptr) { + return Error::InvalidArgument; + } + if (regex_patterns_.empty()) { + return true; + } + for (const auto& regex : regex_patterns_) { + if (RE2::FullMatch(name, *regex)) { + return true; + } + } + return false; +} + +Result ETDumpFilter::filter_delegate_debug_index_( + DelegateDebugIntId debug_handle) { + if (debug_handle == kUnsetDelegateDebugIntId) { + return Error::InvalidArgument; // Delegate debug index is unset + } + + if (range_start_ == 0 && range_end_ == 0) { + return true; + } + + if (debug_handle < range_start_ || debug_handle >= range_end_) { + return false; + } + + return true; +} + +Result ETDumpFilter::filter( + const char* name, + DelegateDebugIntId delegate_debug_index) { + if ((name == nullptr) == (delegate_debug_index == kUnsetDelegateDebugIntId)) { + return Error::InvalidArgument; // Name and delegate debug index should be + // both set or unset + } + + if (name) { + return filter_name_(name); + } else { + return filter_delegate_debug_index_(delegate_debug_index); + } +} + +size_t ETDumpFilter::get_n_regex() const { + return regex_patterns_.size(); +} + +} // namespace etdump +} // namespace executorch diff --git a/devtools/etdump/etdump_filter.h b/devtools/etdump/etdump_filter.h new file mode 100644 index 00000000000..545823a5556 --- /dev/null +++ b/devtools/etdump/etdump_filter.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace executorch::etdump { + +using ::executorch::aten::string_view; +using ::executorch::runtime::Result; + +/** + * ETDumpFilter is a class that filters intermediate output based on output's + * name by full regex filtering, or delegate debug indices by range-based + * filtering. + * + * Note that this filter supports up to MAX_REGEX_PATTERNS regex patterns with a + * maximum length of MAX_PATTERN_LENGTH characters each. + */ + +class ETDumpFilter : public ::executorch::runtime::EventTracerFilterBase { + public: + ETDumpFilter(); + ~ETDumpFilter() override = default; + /** + * Adds a regex pattern to the filter. + * + * @param[in] pattern A c string representing the regex pattern to be added. + * + * @return A Result indicating the success or failure of adding the + * regex pattern. + * - True if the pattern is successfully added. + * - False if the pattern could not be added or if the maximum number + * of patterns is exceeded. + * - An error code if number of pattern has reached to cap, or any + * error occurs during regex compilation. + */ + Result add_regex(string_view pattern); + /** + * Sets the range for the delegate debug index filtering as [start, end). + * Note that this function will flush the existing range. + * + * @param[in] start The start of the range for filtering. + * @param[in] end The end of the range for filtering. + * + * @return A Result indicating the success or failure of setting the + * range. + * - True if the range is successfully set. + * - An error code if an error occurs. + */ + Result set_debug_handle_range(size_t start, size_t end); + + /** + * Filters events based on the given name or delegate debug index. + * + * Note that everytime only one of either the name or delegate_debug_index + * should be passed in. + * + * @param[in] name A pointer to a string representing the `name` of the + * event. If `delegate_debug_index` is not set to kUnsetDebugHandle, `name` + * should be set to nullptr. + * + * @param[in] delegate_debug_index A DebugHandle representing the debug index + * of the delegate. If `name` is not nullptr, this should be set to + * kUnsetDebugHandle. + * + * @return A Result indicating whether the event matches the filter + * criteria. + * - True if the event matches the filter, or filter is unset. + * - False if the event does not match or is unknown. + * - An error code if an error occurs during filtering. + */ + Result filter( + const char* name, + ::executorch::runtime::DelegateDebugIntId delegate_debug_index) override; + + /** + * Returns the number of regex patterns in the filter. + */ + size_t get_n_regex() const; + + private: + std::vector> regex_patterns_; + size_t range_start_ = 0; + size_t range_end_ = 0; + Result filter_name_(const char* name); + Result filter_delegate_debug_index_( + ::executorch::runtime::DelegateDebugIntId delegate_debug_index); +}; + +} // namespace executorch::etdump diff --git a/devtools/etdump/etdump_flatcc.cpp b/devtools/etdump/etdump_flatcc.cpp index 5f419ba0503..ea464f2f5ce 100644 --- a/devtools/etdump/etdump_flatcc.cpp +++ b/devtools/etdump/etdump_flatcc.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ using ::executorch::runtime::ChainID; using ::executorch::runtime::DebugHandle; using ::executorch::runtime::DelegateDebugIdType; using ::executorch::runtime::DelegateDebugIntId; +using ::executorch::runtime::Error; using ::executorch::runtime::EValue; using ::executorch::runtime::EventTracerEntry; using ::executorch::runtime::kUnsetDelegateDebugIntId; diff --git a/devtools/etdump/etdump_flatcc.h b/devtools/etdump/etdump_flatcc.h index b3588edbc0b..6b51745eee3 100644 --- a/devtools/etdump/etdump_flatcc.h +++ b/devtools/etdump/etdump_flatcc.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include diff --git a/devtools/etdump/targets.bzl b/devtools/etdump/targets.bzl index dda68e1b6ac..04a449f3920 100644 --- a/devtools/etdump/targets.bzl +++ b/devtools/etdump/targets.bzl @@ -101,6 +101,27 @@ def define_common_targets(): for aten_mode in get_aten_mode_options(): aten_suffix = "_aten" if aten_mode else "" + runtime.cxx_library( + name = "etdump_filter" + aten_suffix, + srcs = [ + "etdump_filter.cpp", + ], + exported_headers = [ + "etdump_filter.h", + ], + deps = [ + "//executorch/runtime/platform:platform", + ], + exported_deps = [ + "fbsource//third-party/re2:re2", + "//executorch/runtime/core:event_tracer" + aten_suffix, + ], + visibility = [ + "//executorch/...", + "@EXECUTORCH_CLIENTS", + ], + ) + runtime.cxx_library( name = "etdump_flatcc" + aten_suffix, srcs = [ diff --git a/devtools/etdump/tests/etdump_filter_test.cpp b/devtools/etdump/tests/etdump_filter_test.cpp new file mode 100644 index 00000000000..4e7e87da136 --- /dev/null +++ b/devtools/etdump/tests/etdump_filter_test.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +#include + +using ::executorch::etdump::ETDumpFilter; +using ::executorch::runtime::Error; +using ::executorch::runtime::kUnsetDelegateDebugIntId; +using ::executorch::runtime::Result; + +class ETDumpFilterTest : public ::testing::Test { + protected: + ETDumpFilter filter; + + void SetUp() override { + torch::executor::runtime_init(); + } + + void TearDown() override {} +}; + +TEST_F(ETDumpFilterTest, AddRegexPatternSuccess) { + Result result = filter.add_regex("test.*"); + EXPECT_TRUE(result.ok()); + EXPECT_TRUE(result.get()); +} + +TEST_F(ETDumpFilterTest, SetDebugHandleRangeSuccess) { + Result result = filter.set_debug_handle_range(10, 20); + EXPECT_TRUE(result.ok()); + EXPECT_TRUE(result.get()); +} + +TEST_F(ETDumpFilterTest, SetDebugHandleRangeFailure) { + Result result = filter.set_debug_handle_range(20, 10); + EXPECT_EQ(result.error(), Error::InvalidArgument); +} + +TEST_F(ETDumpFilterTest, FilterByNameSuccess) { + filter.add_regex("event.*"); + Result result = filter.filter("event_name", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result.ok()); + EXPECT_TRUE(result.get()); +} + +TEST_F(ETDumpFilterTest, PartialMatchingFailed) { + filter.add_regex("event.*"); + Result result = + filter.filter("non_matching_event", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result.ok()); + EXPECT_FALSE(result.get()); +} + +TEST_F(ETDumpFilterTest, FilterByDelegateDebugIndexSuccess) { + filter.set_debug_handle_range(10, 20); + Result result = filter.filter(nullptr, 15); + EXPECT_TRUE(result.ok()); + EXPECT_TRUE(result.get()); +} + +TEST_F(ETDumpFilterTest, FilterByDelegateDebugIndexFailure) { + filter.set_debug_handle_range(10, 20); + Result result = filter.filter(nullptr, 25); + EXPECT_TRUE(result.ok()); + EXPECT_FALSE(result.get()); +} + +TEST_F(ETDumpFilterTest, NaiveFilterNameInputCanSucceed) { + Result result = filter.filter("any_input", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result.ok()); + EXPECT_TRUE(result.get()); +} + +TEST_F(ETDumpFilterTest, NaiveFilterDebugHandleInputCanSucceed) { + Result result = filter.filter(nullptr, 12345); + EXPECT_TRUE(result.ok()); + EXPECT_TRUE(result.get()); +} + +TEST_F(ETDumpFilterTest, IllegalInput) { + filter.add_regex("pattern"); + Result result = filter.filter("matching_event", 1); + EXPECT_EQ(result.error(), Error::InvalidArgument); +} + +TEST_F(ETDumpFilterTest, NoMatchFirstThenMatch) { + filter.add_regex("non_matching_pattern"); + Result result_1 = + filter.filter("matching_event", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result_1.ok()); + EXPECT_FALSE(result_1.get()); + filter.add_regex("matching_.*"); + Result result_2 = + filter.filter("matching_event", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result_2.ok()); + EXPECT_TRUE(result_2.get()); +} + +TEST_F(ETDumpFilterTest, MatchRegexFirstThen) { + filter.add_regex("matching.*"); + Result result_1 = + filter.filter("matching_event", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result_1.ok()); + EXPECT_TRUE(result_1.get()); + filter.add_regex("non_matching_pattern"); + Result result_2 = + filter.filter("matching_event", kUnsetDelegateDebugIntId); + EXPECT_TRUE(result_2.ok()); + EXPECT_TRUE(result_2.get()); +} diff --git a/devtools/etdump/tests/targets.bzl b/devtools/etdump/tests/targets.bzl index c97696c6c82..7f266eed5a7 100644 --- a/devtools/etdump/tests/targets.bzl +++ b/devtools/etdump/tests/targets.bzl @@ -21,3 +21,14 @@ def define_common_targets(): "//executorch/runtime/core/exec_aten/testing_util:tensor_util", ], ) + + runtime.cxx_test( + name = "etdump_filter_test", + srcs = [ + "etdump_filter_test.cpp", + ], + deps = [ + "//executorch/devtools/etdump:etdump_filter", + "//executorch/runtime/platform:platform", + ], + ) diff --git a/runtime/core/event_tracer.h b/runtime/core/event_tracer.h index eecd1381e79..77d7fc64102 100644 --- a/runtime/core/event_tracer.h +++ b/runtime/core/event_tracer.h @@ -101,14 +101,14 @@ class EventTracerFilterBase { * - An error code if an error occurs during filtering. */ virtual Result filter( - char* name, - DelegateDebugIntId delegate_debug_index); + const char* name, + DelegateDebugIntId delegate_debug_index) = 0; /** * Virtual destructor for the EventTracerFilterBase class. * Ensures proper cleanup of derived class objects. */ - virtual ~EventTracerFilterBase(); + virtual ~EventTracerFilterBase() = default; }; /**