Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry pick PR #2877: Add logTrace API for Kabuki to log instrumentation events through JS h5vcc API #2938

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cobalt/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ group("gn_all") {
"//cobalt/renderer/sandbox:scaling_text_sandbox",
"//cobalt/speech/sandbox:speech_sandbox",
"//cobalt/ui_navigation/scroll_engine:scroll_engine_tests",
"//cobalt/watchdog:watchdog_test",
"//cobalt/web:web_test",
"//cobalt/web_animations:web_animations_test",
"//cobalt/webdriver:webdriver_test",
Expand Down
30 changes: 30 additions & 0 deletions cobalt/h5vcc/h5vcc_crash_log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,35 @@ void H5vccCrashLog::SetPersistentSettingWatchdogCrash(bool can_trigger_crash) {
if (watchdog) watchdog->SetPersistentSettingWatchdogCrash(can_trigger_crash);
}

bool H5vccCrashLog::LogEvent(const std::string& event) {
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
if (!watchdog) {
return false;
}

return watchdog->LogEvent(event);
}

script::Sequence<std::string> H5vccCrashLog::GetLogTrace() {
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();

script::Sequence<std::string> sequence;
if (watchdog) {
std::vector<std::string> logTrace = watchdog->GetLogTrace();
for (std::size_t i = 0; i < logTrace.size(); ++i) {
sequence.push_back(logTrace[i]);
}
}

return sequence;
}

void H5vccCrashLog::ClearLog() {
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
if (watchdog) {
watchdog->ClearLog();
}
}

} // namespace h5vcc
} // namespace cobalt
6 changes: 6 additions & 0 deletions cobalt/h5vcc/h5vcc_crash_log.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ class H5vccCrashLog : public script::Wrappable {

void SetPersistentSettingWatchdogCrash(bool can_trigger_crash);

bool LogEvent(const std::string& event);

script::Sequence<std::string> GetLogTrace();

void ClearLog();

DEFINE_WRAPPABLE_TYPE(H5vccCrashLog);

private:
Expand Down
15 changes: 15 additions & 0 deletions cobalt/h5vcc/h5vcc_crash_log.idl
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,19 @@ interface H5vccCrashLog {
// Sets a persistent Watchdog setting that determines whether or not a
// Watchdog violation will trigger a crash.
void setPersistentSettingWatchdogCrash(boolean can_trigger_crash);

// Appends a string event to a ring buffer. These log events can be appended
// from JS code. When watchdog violation is created, a snapshot of
// that buffer is attached to a violation. Identical sequential events
// are de-duplicated.
// Max event length is 256. Ring buffer size is 128.
boolean logEvent(DOMString event);

// Returns a snapshot (a "logTrace") of ring buffer of log events.
// This can be used to read a logTrace at arbitrary moment,
// without any violation.
sequence<DOMString> getLogTrace();

// Clears the ring buffer of log events.
void clearLog();
};
7 changes: 6 additions & 1 deletion cobalt/watchdog/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

static_library("watchdog") {
sources = [
"instrumentation_log.cc",
"instrumentation_log.h",
"watchdog.cc",
"watchdog.h",
]
Expand All @@ -29,7 +31,10 @@ static_library("watchdog") {
target(gtest_target_type, "watchdog_test") {
testonly = true

sources = [ "watchdog_test.cc" ]
sources = [
"instrumentation_log_test.cc",
"watchdog_test.cc",
]

deps = [
":watchdog",
Expand Down
63 changes: 63 additions & 0 deletions cobalt/watchdog/instrumentation_log.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2024 The Cobalt Authors. 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/watchdog/instrumentation_log.h"

#include <string>
#include <vector>

namespace cobalt {
namespace watchdog {

bool InstrumentationLog::LogEvent(const std::string& event) {
if (event.length() > kMaxEventLenBytes) {
SB_DLOG(ERROR) << "[Watchdog] Log event exceeds max: " << kMaxEventLenBytes;
return false;
}

starboard::ScopedLock scoped_lock(buffer_mutex_);
buffer_.SaveToBuffer(event);

return true;
}

std::vector<std::string> InstrumentationLog::GetLogTrace() {
std::vector<std::string> traceEvents;

starboard::ScopedLock scoped_lock(buffer_mutex_);
for (auto it = buffer_.Begin(); it; ++it) {
traceEvents.push_back(**it);
}

return traceEvents;
}

base::Value InstrumentationLog::GetLogTraceAsValue() {
base::Value log_trace_value = base::Value(base::Value::Type::LIST);

starboard::ScopedLock scoped_lock(buffer_mutex_);
for (auto it = buffer_.Begin(); it; ++it) {
log_trace_value.GetList().emplace_back(**it);
}

return log_trace_value;
}

void InstrumentationLog::ClearLog() {
starboard::ScopedLock scoped_lock(buffer_mutex_);
buffer_.Clear();
}

} // namespace watchdog
} // namespace cobalt
59 changes: 59 additions & 0 deletions cobalt/watchdog/instrumentation_log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2024 The Cobalt Authors. 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.

#ifndef COBALT_WATCHDOG_INSTRUMENTATION_LOG_H_
#define COBALT_WATCHDOG_INSTRUMENTATION_LOG_H_

#include <string>
#include <vector>

#include "base/containers/ring_buffer.h"
#include "base/values.h"
#include "starboard/common/mutex.h"

namespace cobalt {
namespace watchdog {

constexpr int kBufferSize = 128;
constexpr int kMaxEventLenBytes = 256;

// Wrapper class on top of base::RingBuffer for tracking log events emitted
// through logEvent() h5vcc API. There's an optimization: identical sequential
// events added back to back are folded into single event.
class InstrumentationLog {
public:
// Append a single event to the end of the buffer.
bool LogEvent(const std::string& event);

// Returns a snapshot of the ring buffer.
// Vector of kBufferSize strings at max.
std::vector<std::string> GetLogTrace();

// Same as GetLogTrace() but converted to base::Value for json serialization.
base::Value GetLogTraceAsValue();

// Empty the ring buffer.
void ClearLog();

private:
base::RingBuffer<std::string, kBufferSize> buffer_;

// Mutex to guard buffer operations. E.g. LogEvent() called from h5vcc API
// handler and getLogTrace() called from watchdog monitoring thread.
starboard::Mutex buffer_mutex_;
};

} // namespace watchdog
} // namespace cobalt
#endif // COBALT_WATCHDOG_INSTRUMENTATION_LOG_H_
122 changes: 122 additions & 0 deletions cobalt/watchdog/instrumentation_log_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2024 The Cobalt Authors. 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/watchdog//instrumentation_log.h"

#include <set>
#include <vector>

#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "starboard/common/file.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cobalt {
namespace watchdog {

class InstrumentationLogTest : public testing::Test {
protected:
InstrumentationLog* instrumentation_log_;
};

TEST_F(InstrumentationLogTest, CanCallLogEvent) {
InstrumentationLog log;
log.LogEvent("abc");

ASSERT_EQ(log.GetLogTrace().size(), 1);
}

TEST_F(InstrumentationLogTest, LogEventReturnsFalseOnMaxLenExceed) {
InstrumentationLog log;

std::string maxLenEvent(kMaxEventLenBytes, 'a');
ASSERT_TRUE(log.LogEvent(maxLenEvent));

std::string exceedingLenEvent(kMaxEventLenBytes + 1, 'a');
ASSERT_FALSE(log.LogEvent(exceedingLenEvent));
}

TEST_F(InstrumentationLogTest, GetLogTraceReturnsEventsInOrder) {
InstrumentationLog log;

for (int i = 0; i < kBufferSize; i++) {
log.LogEvent(std::to_string(i));
}

log.LogEvent("1");
log.LogEvent("2");
log.LogEvent("3");
log.LogEvent("4");
log.LogEvent("5");

ASSERT_EQ(log.GetLogTrace().at(kBufferSize - 1 - 4), "1");
ASSERT_EQ(log.GetLogTrace().at(kBufferSize - 1 - 3), "2");
ASSERT_EQ(log.GetLogTrace().at(kBufferSize - 1 - 2), "3");
ASSERT_EQ(log.GetLogTrace().at(kBufferSize - 1 - 1), "4");
ASSERT_EQ(log.GetLogTrace().at(kBufferSize - 1), "5");
}

TEST_F(InstrumentationLogTest, GetLogTraceCanReturnDuplicateEvents) {
InstrumentationLog log;

log.LogEvent("1");
log.LogEvent("1");
log.LogEvent("1");

ASSERT_EQ(log.GetLogTrace().at(0), "1");
ASSERT_EQ(log.GetLogTrace().at(1), "1");
ASSERT_EQ(log.GetLogTrace().at(2), "1");
}

TEST_F(InstrumentationLogTest, CanGetEmptyTraceAsValue) {
InstrumentationLog log;

ASSERT_EQ(log.GetLogTraceAsValue().GetList().size(), 0);
}

TEST_F(InstrumentationLogTest, CanGetLogTraceAsValue) {
InstrumentationLog log;

log.LogEvent("1");
log.LogEvent("2");
log.LogEvent("3");

ASSERT_EQ(log.GetLogTraceAsValue().GetList().at(0).GetString(), "1");
ASSERT_EQ(log.GetLogTraceAsValue().GetList().at(1).GetString(), "2");
ASSERT_EQ(log.GetLogTraceAsValue().GetList().at(2).GetString(), "3");
}

TEST_F(InstrumentationLogTest, CanClearLog) {
InstrumentationLog log;

log.LogEvent("1");
log.LogEvent("2");
log.LogEvent("3");

ASSERT_EQ(log.GetLogTrace().size(), 3);

log.ClearLog();
ASSERT_EQ(log.GetLogTrace().size(), 0);
}

TEST_F(InstrumentationLogTest, CanClearEmptyLog) {
InstrumentationLog log;
ASSERT_EQ(log.GetLogTrace().size(), 0);

log.ClearLog();
ASSERT_EQ(log.GetLogTrace().size(), 0);
}

} // namespace watchdog
} // namespace cobalt
Loading
Loading