| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/trace_event/scoped_event_parser_trace.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::InSequence; |
| using ::testing::Invoke; |
| using ::testing::_; |
| using base::debug::TraceLog; |
| using cobalt::trace_event::EventParser; |
| using cobalt::trace_event::ScopedEventParserTrace; |
| |
| // The tests in this file are all designed in a similar format that has three |
| // stages. |
| // |
| // 1) One or more Analyze() functions are defined which specify a function |
| // for analyzing and testing the results of each finish ScopedEvent as they |
| // arrive. |
| // |
| // 2) An expected sequence of flow end events is defined and they are setup |
| // to each call the analysis function for further test verification of |
| // the produced flow end event. |
| // |
| // 3) Finally a EventParserTest::Recorder object is constructed which starts |
| // the recording of events, after which a number of TRACE commands are |
| // issued that ultimately result in the pattern to be analyzed in stages 1 |
| // and 2. |
| |
| namespace { |
| |
| class MockEventParserEventHandler { |
| public: |
| MOCK_METHOD1(HandleFlowEndEvent, |
| void(const scoped_refptr<EventParser::ScopedEvent>& event)); |
| }; |
| |
| class EventParserTest : public ::testing::Test { |
| public: |
| virtual ~EventParserTest() {} |
| |
| protected: |
| class Recorder { |
| public: |
| explicit Recorder(MockEventParserEventHandler* event_handler) |
| : event_watcher_( |
| base::Bind(&MockEventParserEventHandler::HandleFlowEndEvent, |
| base::Unretained(event_handler))) {} |
| |
| private: |
| ScopedEventParserTrace event_watcher_; |
| }; |
| |
| MockEventParserEventHandler event_handler_; |
| }; |
| |
| // Useful for using base::Bind() along with GMock actions. |
| ACTION_P(InvokeCallback1, callback) { callback.Run(arg0); } |
| |
| const char* kNullCharP = NULL; |
| |
| // Helper function for finding the latest end time amongst the given event |
| // and all its children. This is useful for testing that the given event's |
| // end flow time is equal to this value. |
| void ExpectLatestEndTimeOfDescendantsEqualToEndFlowTime( |
| const scoped_refptr<EventParser::ScopedEvent>& event) { |
| base::TimeTicks latest = event->end_event()->timestamp(); |
| for (size_t i = 0; i < event->children().size(); ++i) { |
| ExpectLatestEndTimeOfDescendantsEqualToEndFlowTime(event->children()[i]); |
| |
| base::TimeTicks child_latest = *event->children()[i]->end_flow_time(); |
| if (child_latest - latest > base::TimeDelta()) { |
| latest = child_latest; |
| } |
| } |
| ASSERT_TRUE(static_cast<bool>(event->end_flow_time())); |
| DCHECK_EQ(event->end_flow_time()->ToInternalValue(), |
| latest.ToInternalValue()); |
| } |
| |
| // SimpleAnalyzeEvent performs a basic integrity check on the event passed in. |
| // In particular, it checks that the event's name matches the passed in |
| // event_name, that the event's parent's name matches the passed in parent_name |
| // or has no parent if parent_name is NULL, and finally it checks that the |
| // event has the specified number of children. |
| void SimpleAnalyzeEvent(const char* event_name, const char* parent_name, |
| int num_children, |
| const scoped_refptr<EventParser::ScopedEvent>& event) { |
| // Ensure that the correct names have been stored in the begin event. |
| EXPECT_STREQ(event_name, event->begin_event().name()); |
| |
| // Ensure that the end event exists and has the correct names stored. |
| EXPECT_TRUE(static_cast<bool>(event->end_event())); |
| EXPECT_STREQ(event_name, event->end_event()->name()); |
| |
| // The end event should always have a time tick larger than the begin |
| // event. |
| EXPECT_LE(base::TimeDelta(), |
| event->end_event()->timestamp() - event->begin_event().timestamp()); |
| |
| if (parent_name) { |
| ASSERT_TRUE(event->parent()); |
| EXPECT_STREQ(parent_name, event->parent()->begin_event().name()); |
| |
| // Test that we are one of our parents' children. |
| const std::vector<scoped_refptr<EventParser::ScopedEvent> >& children = |
| event->parent()->children(); |
| bool found = false; |
| for (size_t i = 0; i < children.size(); ++i) { |
| if (children[i] == event) { |
| found = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found); |
| } |
| |
| EXPECT_EQ(num_children, event->children().size()); |
| |
| // Check that our end flow time is equal to the end time of one of our |
| // descendants with the latest end time. |
| ExpectLatestEndTimeOfDescendantsEqualToEndFlowTime(event); |
| } |
| |
| } // namespace |
| |
| TEST_F(EventParserTest, SingleSimpleScopedEventTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event", kNullCharP, 0))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT0("EventParserTest", "test_event"); |
| } |
| |
| TEST_F(EventParserTest, MultipleSiblingScopedEventTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_1", kNullCharP, 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_2", kNullCharP, 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_3", kNullCharP, 0))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| { TRACE_EVENT0("EventParserTest", "test_event_1"); } |
| { TRACE_EVENT0("EventParserTest", "test_event_2"); } |
| { TRACE_EVENT0("EventParserTest", "test_event_3"); } |
| } |
| |
| TEST_F(EventParserTest, SimpleNestedScopedEventsTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_3", "test_event_2", 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_2", "test_event_1", 1))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_1", kNullCharP, 1))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT0("EventParserTest", "test_event_1"); |
| TRACE_EVENT0("EventParserTest", "test_event_2"); |
| TRACE_EVENT0("EventParserTest", "test_event_3"); |
| } |
| |
| TEST_F(EventParserTest, MultipleNestedScopedEventsTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_2", "test_event_1", 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_3", "test_event_1", 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_1", kNullCharP, 2))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT0("EventParserTest", "test_event_1"); |
| { TRACE_EVENT0("EventParserTest", "test_event_2"); } |
| { TRACE_EVENT0("EventParserTest", "test_event_3"); } |
| } |
| |
| TEST_F(EventParserTest, SimpleFlowTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow", kNullCharP, 0))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow", 1); |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow", 1); |
| } |
| |
| TEST_F(EventParserTest, ParallelFlowTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow_1", kNullCharP, 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow_3", kNullCharP, 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow_2", kNullCharP, 0))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow_1", 1); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow_2", 2); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow_3", 3); |
| |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow_1", 1); |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow_3", 3); |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow_2", 2); |
| } |
| |
| namespace { |
| void AnalyzeFlowSteps(const char* event_name, const char* step_1_name, |
| const char* step_2_name, |
| const scoped_refptr<EventParser::ScopedEvent>& event) { |
| SimpleAnalyzeEvent(event_name, kNullCharP, 0, event); |
| |
| ASSERT_EQ(2, event->instant_events().size()); |
| EXPECT_STREQ(step_1_name, |
| event->instant_events()[0].arg_values()[0].as_string); |
| EXPECT_STREQ(step_2_name, |
| event->instant_events()[1].arg_values()[0].as_string); |
| } |
| } // namespace |
| |
| TEST_F(EventParserTest, FlowHasStepTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce( |
| InvokeCallback1(base::Bind(&AnalyzeFlowSteps, "test_flow", |
| "test_flow_step_1", "test_flow_step_2"))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow", 1); |
| TRACE_EVENT_FLOW_STEP0( |
| "EventParserTest", "test_flow", 1, "test_flow_step_1"); |
| TRACE_EVENT_FLOW_STEP0( |
| "EventParserTest", "test_flow", 1, "test_flow_step_2"); |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow", 1); |
| } |
| |
| // Flows will be children of normal scoped events if they are *begun* within |
| // that scope. In this case, all future TRACE_EVENT_FLOW_STEP and |
| // TRACE_EVENT_FLOW_END calls will refer to the same flow event with the |
| // original parent from the call to TRACE_EVENT_FLOW_BEGIN. Note that flows |
| // can extend a scoped event's flow end time beyond the scopes end time. |
| TEST_F(EventParserTest, FlowIsChildOfEventTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_2", kNullCharP, 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow", "test_event_1", 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_1", kNullCharP, 1))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_event_3", kNullCharP, 0))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| { |
| TRACE_EVENT0("EventParserTest", "test_event_1"); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow", 1); |
| } |
| |
| { |
| TRACE_EVENT0("EventParserTest", "test_event_2"); |
| TRACE_EVENT_FLOW_STEP0("EventParserTest", "test_flow", 1, |
| "test_flow_step_1"); |
| } |
| |
| { |
| TRACE_EVENT0("EventParserTest", "test_event_3"); |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow", 1); |
| } |
| } |
| |
| // It is possible to link an event as a child of a flow step by declaring |
| // the event immediately after the call to TRACE_EVENT_FLOW_STEP, and assigning |
| // the event the same name as the flow step. |
| TEST_F(EventParserTest, EventIsChildOfFlowStepTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow_step_1", "test_flow", 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce(InvokeCallback1( |
| base::Bind(&SimpleAnalyzeEvent, "test_flow", kNullCharP, 1))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "test_flow", 1); |
| |
| { |
| TRACE_EVENT_FLOW_STEP0("EventParserTest", "test_flow", 1, |
| "test_flow_step_1"); |
| TRACE_EVENT0("EventParserTest", "test_flow_step_1"); |
| } |
| |
| TRACE_EVENT_FLOW_END0("EventParserTest", "test_flow", 1); |
| } |
| |
| // As a special case to support Chromium's base::MessageLoop tracing |
| // integration, it is possible to link an event as a child of a flow by |
| // declaring an event with the name "MessageLoop::RunTask" immediately after |
| // ending a flow with the name "MessageLoop::PostTask". |
| TEST_F(EventParserTest, EventIsChildOfFlowEndPostTaskTest) { |
| { |
| InSequence s; |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)).WillOnce( |
| InvokeCallback1(base::Bind(&SimpleAnalyzeEvent, "MessageLoop::RunTask", |
| "MessageLoop::PostTask", 0))); |
| EXPECT_CALL(event_handler_, HandleFlowEndEvent(_)) |
| .WillOnce(InvokeCallback1(base::Bind( |
| &SimpleAnalyzeEvent, "MessageLoop::PostTask", kNullCharP, 1))); |
| } |
| |
| Recorder recorder(&event_handler_); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "MessageLoop::PostTask", 1); |
| TRACE_EVENT_FLOW_END0("EventParserTest", "MessageLoop::PostTask", 1); |
| TRACE_EVENT0("EventParserTest", "MessageLoop::RunTask"); |
| } |
| |
| namespace { |
| void NopEventHandler(const scoped_refptr<EventParser::ScopedEvent>& event) { |
| UNREFERENCED_PARAMETER(event); |
| } |
| } // namespace |
| |
| TEST_F(EventParserTest, VeryLongFlowChainsDoNotStackOverflow) { |
| // This test ensures that when we construct very long chains of flow events, |
| // the logic for processing the results does not result in a stack overflow |
| // due to recursive calls. Since GMock's "InSequence" also constructs |
| // a tree model, it would crash in this test due to stack overflow, so it is |
| // not used. |
| static const int kFlowLength = 2000; |
| |
| ScopedEventParserTrace event_watcher(base::Bind(&NopEventHandler)); |
| for (int i = 0; i < kFlowLength; ++i) { |
| if (i != 0) { |
| TRACE_EVENT_FLOW_END0("EventParserTest", "MessageLoop::PostTask", i - 1); |
| } |
| TRACE_EVENT0("EventParserTest", "MessageLoop::RunTask"); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "MessageLoop::PostTask", i); |
| } |
| TRACE_EVENT_FLOW_END0("EventParserTest", "MessageLoop::PostTask", |
| kFlowLength - 1); |
| TRACE_EVENT0("EventParserTest", "MessageLoop::RunTask"); |
| } |
| |
| TEST_F(EventParserTest, VeryLongFlowChainsDoNotStackOverflowOnAbort) { |
| // This test is similar to VeryLongFlowChainsDoNotStackOverflow except it |
| // deals with events that are aborted versus events that are ended naturally. |
| static const int kFlowLength = 2000; |
| |
| base::optional<ScopedEventParserTrace> event_watcher( |
| base::in_place, base::Bind(&NopEventHandler)); |
| for (int i = 0; i < kFlowLength; ++i) { |
| if (i != 0) { |
| TRACE_EVENT_FLOW_END0("EventParserTest", "MessageLoop::PostTask", i - 1); |
| } |
| TRACE_EVENT0("EventParserTest", "MessageLoop::RunTask"); |
| TRACE_EVENT_FLOW_BEGIN0("EventParserTest", "MessageLoop::PostTask", i); |
| } |
| TRACE_EVENT_FLOW_END0("EventParserTest", "MessageLoop::PostTask", |
| kFlowLength - 1); |
| TRACE_EVENT0("EventParserTest", "MessageLoop::RunTask"); |
| |
| // Destroy the event watcher before the last TRACE_EVENT0() scope closes, |
| // triggering an abort for the unfinished event flows. |
| event_watcher = base::nullopt; |
| } |