blob: eaf1ae096b7218da700e77a5626762d8c82b9f9a [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fixture.h"
#include <threads.h>
#include <fbl/function.h>
#include <fbl/string.h>
#include <fbl/string_printf.h>
#include <fbl/vector.h>
#include <zx/event.h>
#include <trace-engine/instrumentation.h>
namespace {
int RunClosure(void* arg) {
auto closure = static_cast<fbl::Closure*>(arg);
(*closure)();
delete closure;
return 0;
}
void RunThread(fbl::Closure closure) {
thrd_t thread;
int result = thrd_create(&thread, RunClosure,
new fbl::Closure(fbl::move(closure)));
ZX_ASSERT(result == thrd_success);
result = thrd_join(thread, nullptr);
ZX_ASSERT(result == thrd_success);
}
bool test_normal_shutdown() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
fixture_stop_tracing();
EXPECT_EQ(ZX_OK, fixture_get_disposition());
END_TRACE_TEST;
}
bool test_hard_shutdown() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
fixture_stop_tracing_hard();
EXPECT_EQ(ZX_ERR_CANCELED, fixture_get_disposition());
END_TRACE_TEST;
}
bool test_state() {
BEGIN_TRACE_TEST;
EXPECT_EQ(TRACE_STOPPED, trace_state());
fixture_start_tracing();
EXPECT_EQ(TRACE_STARTED, trace_state());
fixture_stop_tracing();
EXPECT_EQ(TRACE_STOPPED, trace_state());
END_TRACE_TEST;
}
bool test_is_enabled() {
BEGIN_TRACE_TEST;
EXPECT_FALSE(trace_is_enabled(), "");
fixture_start_tracing();
EXPECT_TRUE(trace_is_enabled(), "");
fixture_stop_tracing();
EXPECT_FALSE(trace_is_enabled(), "");
END_TRACE_TEST;
}
bool test_is_category_enabled() {
BEGIN_TRACE_TEST;
EXPECT_FALSE(trace_is_category_enabled("+enabled"), "");
EXPECT_FALSE(trace_is_category_enabled("-disabled"), "");
EXPECT_FALSE(trace_is_category_enabled(""), "");
fixture_start_tracing();
EXPECT_TRUE(trace_is_category_enabled("+enabled"), "");
EXPECT_FALSE(trace_is_category_enabled("-disabled"), "");
EXPECT_FALSE(trace_is_category_enabled(""), "");
fixture_stop_tracing();
EXPECT_FALSE(trace_is_category_enabled("+enabled"), "");
EXPECT_FALSE(trace_is_category_enabled("-disabled"), "");
EXPECT_FALSE(trace_is_category_enabled(""), "");
END_TRACE_TEST;
}
bool test_generate_nonce() {
BEGIN_TRACE_TEST;
uint64_t nonce1 = trace_generate_nonce();
EXPECT_NE(0u, nonce1, "nonce is never 0");
uint64_t nonce2 = trace_generate_nonce();
EXPECT_NE(0u, nonce2, "nonce is never 0");
EXPECT_NE(nonce1, nonce2, "nonce is unique");
END_TRACE_TEST;
}
bool test_observer() {
BEGIN_TRACE_TEST;
zx::event event;
EXPECT_EQ(ZX_OK, zx::event::create(0u, &event));
EXPECT_EQ(ZX_OK, trace_register_observer(event.get()));
EXPECT_EQ(ZX_ERR_TIMED_OUT, event.wait_one(ZX_EVENT_SIGNALED, 0u, nullptr));
fixture_start_tracing();
EXPECT_EQ(ZX_OK, event.wait_one(ZX_EVENT_SIGNALED, 0u, nullptr));
EXPECT_EQ(ZX_OK, event.signal(ZX_EVENT_SIGNALED, 0u));
EXPECT_EQ(ZX_ERR_TIMED_OUT, event.wait_one(ZX_EVENT_SIGNALED, 0u, nullptr));
fixture_stop_tracing();
EXPECT_EQ(ZX_OK, event.wait_one(ZX_EVENT_SIGNALED, 0u, nullptr));
EXPECT_EQ(ZX_OK, trace_unregister_observer(event.get()));
END_TRACE_TEST;
}
bool test_observer_errors() {
BEGIN_TRACE_TEST;
zx::event event;
EXPECT_EQ(ZX_OK, zx::event::create(0u, &event));
EXPECT_EQ(ZX_OK, trace_register_observer(event.get()));
EXPECT_EQ(ZX_ERR_INVALID_ARGS, trace_register_observer(event.get()));
EXPECT_EQ(ZX_OK, trace_unregister_observer(event.get()));
EXPECT_EQ(ZX_ERR_NOT_FOUND, trace_unregister_observer(event.get()));
END_TRACE_TEST;
}
bool test_register_current_thread() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
trace_thread_ref_t t1, t2;
{
auto context = trace::TraceContext::Acquire();
trace_context_register_current_thread(context.get(), &t1);
trace_context_register_current_thread(context.get(), &t2);
}
EXPECT_TRUE(trace_is_indexed_thread_ref(&t1));
EXPECT_TRUE(trace_is_indexed_thread_ref(&t2));
EXPECT_EQ(t1.encoded_value, t2.encoded_value);
ASSERT_RECORDS(R"X(String(index: 1, "process")
KernelObject(koid: <>, type: thread, name: "initial-thread", {process: koid(<>)})
Thread(index: 1, <>)
)X",
"");
END_TRACE_TEST;
}
bool test_register_current_thread_multiple_threads() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
trace_thread_ref_t t1;
{
auto context = trace::TraceContext::Acquire();
trace_context_register_current_thread(context.get(), &t1);
}
trace_thread_ref_t t2;
RunThread([&t2] {
auto context = trace::TraceContext::Acquire();
trace_context_register_current_thread(context.get(), &t2);
});
EXPECT_TRUE(trace_is_indexed_thread_ref(&t1));
EXPECT_TRUE(trace_is_indexed_thread_ref(&t2));
EXPECT_NE(t1.encoded_value, t2.encoded_value);
ASSERT_RECORDS(R"X(String(index: 1, "process")
KernelObject(koid: <>, type: thread, name: "initial-thread", {process: koid(<>)})
Thread(index: 1, <>)
String(index: 2, "process")
KernelObject(koid: <>, type: thread, name: "thrd_t:<>/TLS=<>", {process: koid(<>)})
Thread(index: 2, <>)
)X",
"");
END_TRACE_TEST;
}
bool test_register_string_literal() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
trace_string_ref_t empty;
trace_string_ref_t null;
trace_string_ref_t a1, a2, a3;
trace_string_ref_t b1, b2, b3;
{
auto context = trace::TraceContext::Acquire();
trace_context_register_string_literal(context.get(), "", &empty);
trace_context_register_string_literal(context.get(), nullptr, &null);
trace_context_register_string_literal(context.get(), "string1", &a1);
trace_context_register_string_literal(context.get(), "string2", &a2);
trace_context_register_string_literal(context.get(), "string3", &a3);
trace_context_register_string_literal(context.get(), "string1", &b1);
trace_context_register_string_literal(context.get(), "string2", &b2);
trace_context_register_string_literal(context.get(), "string3", &b3);
}
EXPECT_TRUE(trace_is_empty_string_ref(&empty));
EXPECT_TRUE(trace_is_empty_string_ref(&null));
EXPECT_TRUE(trace_is_indexed_string_ref(&a1));
EXPECT_TRUE(trace_is_indexed_string_ref(&a2));
EXPECT_TRUE(trace_is_indexed_string_ref(&a3));
EXPECT_TRUE(trace_is_indexed_string_ref(&b1));
EXPECT_TRUE(trace_is_indexed_string_ref(&b2));
EXPECT_TRUE(trace_is_indexed_string_ref(&b3));
EXPECT_EQ(a1.encoded_value, b1.encoded_value);
EXPECT_EQ(a2.encoded_value, b2.encoded_value);
EXPECT_EQ(a3.encoded_value, b3.encoded_value);
EXPECT_NE(a1.encoded_value, a2.encoded_value);
EXPECT_NE(a1.encoded_value, a3.encoded_value);
EXPECT_NE(a2.encoded_value, a3.encoded_value);
ASSERT_RECORDS(R"X(String(index: 1, "string1")
String(index: 2, "string2")
String(index: 3, "string3")
)X",
"");
END_TRACE_TEST;
}
bool test_register_string_literal_multiple_threads() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
trace_string_ref_t a1;
trace_string_ref_t a2;
{
auto context = trace::TraceContext::Acquire();
trace_context_register_string_literal(context.get(), "string1", &a1);
trace_context_register_string_literal(context.get(), "string2", &a2);
}
trace_string_ref_t b1;
trace_string_ref_t b2;
RunThread([&b1, &b2] {
auto context = trace::TraceContext::Acquire();
trace_context_register_string_literal(context.get(), "string1", &b1);
trace_context_register_string_literal(context.get(), "string2", &b2);
});
EXPECT_TRUE(trace_is_indexed_string_ref(&a1));
EXPECT_TRUE(trace_is_indexed_string_ref(&a2));
EXPECT_TRUE(trace_is_indexed_string_ref(&b1));
EXPECT_TRUE(trace_is_indexed_string_ref(&b2));
EXPECT_NE(a1.encoded_value, a2.encoded_value);
EXPECT_NE(b1.encoded_value, b2.encoded_value);
// Each thread has its own string pool.
EXPECT_NE(a1.encoded_value, b1.encoded_value);
EXPECT_NE(a2.encoded_value, b2.encoded_value);
ASSERT_RECORDS(R"X(String(index: 1, "string1")
String(index: 2, "string2")
String(index: 3, "string1")
String(index: 4, "string2")
)X",
"");
END_TRACE_TEST;
}
bool test_register_string_literal_table_overflow() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
fbl::Vector<fbl::String> strings;
{
auto context = trace::TraceContext::Acquire();
unsigned n = 0;
for (n = 0; n < TRACE_ENCODED_STRING_REF_MAX_INDEX; n++) {
trace_string_ref_t r;
fbl::String string = fbl::StringPrintf("string%d", n);
strings.push_back(string);
trace_context_register_string_literal(context.get(), string.c_str(), &r);
if (trace_is_inline_string_ref(&r))
break;
}
EXPECT_GT(n, 100); // at least 100 string can be cached per thread
}
END_TRACE_TEST;
}
bool test_maximum_record_length() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
{
auto context = trace::TraceContext::Acquire();
EXPECT_NONNULL(trace_context_alloc_record(context.get(), 0));
EXPECT_NONNULL(trace_context_alloc_record(context.get(), 8));
EXPECT_NONNULL(trace_context_alloc_record(context.get(), 16));
EXPECT_NONNULL(trace_context_alloc_record(
context.get(), TRACE_ENCODED_RECORD_MAX_LENGTH));
EXPECT_NULL(trace_context_alloc_record(
context.get(), TRACE_ENCODED_RECORD_MAX_LENGTH + 8));
EXPECT_NULL(trace_context_alloc_record(
context.get(), TRACE_ENCODED_RECORD_MAX_LENGTH + 16));
}
END_TRACE_TEST;
}
bool test_event_with_inline_everything() {
BEGIN_TRACE_TEST;
fixture_start_tracing();
trace_string_ref_t cat = trace_make_inline_c_string_ref("cat");
trace_string_ref_t name = trace_make_inline_c_string_ref("name");
trace_thread_ref_t thread = trace_make_inline_thread_ref(123, 456);
trace_arg_t args[] = {
trace_make_arg(trace_make_inline_c_string_ref("argname"),
trace_make_string_arg_value(trace_make_inline_c_string_ref("argvalue")))};
{
auto context = trace::TraceContext::Acquire();
trace_context_write_instant_event_record(context.get(), zx_ticks_get(),
&thread, &cat, &name,
TRACE_SCOPE_GLOBAL,
args, fbl::count_of(args));
}
ASSERT_RECORDS(R"X(Event(ts: <>, pt: <>, category: "cat", name: "name", Instant(scope: global), {argname: string("argvalue")})
)X",
"");
END_TRACE_TEST;
}
// NOTE: The functions for writing trace records are exercised by other trace tests.
} // namespace
BEGIN_TEST_CASE(engine_tests)
RUN_TEST(test_normal_shutdown)
RUN_TEST(test_hard_shutdown)
RUN_TEST(test_is_enabled)
RUN_TEST(test_is_category_enabled)
RUN_TEST(test_generate_nonce)
RUN_TEST(test_observer)
RUN_TEST(test_observer_errors)
RUN_TEST(test_register_current_thread)
RUN_TEST(test_register_current_thread_multiple_threads)
RUN_TEST(test_register_string_literal)
RUN_TEST(test_register_string_literal_multiple_threads)
RUN_TEST(test_register_string_literal_table_overflow)
RUN_TEST(test_maximum_record_length)
RUN_TEST(test_event_with_inline_everything)
END_TEST_CASE(engine_tests)