pw_trace: Add facade

This change adds the start of the pw_trace facade, include the API and
some basic tests. Currently this only has the gn build file, and still
needs docs.

Change-Id: I76eee7bcdb68e688d2f1c47978916d775ea1e84f
diff --git a/BUILD.gn b/BUILD.gn
index f9c1e7a..227a939 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -62,6 +62,7 @@
     "$dir_pw_status",
     "$dir_pw_stream",
     "$dir_pw_string",
+    "$dir_pw_trace",
     "$dir_pw_unit_test",
     "$dir_pw_varint",
   ]
@@ -101,6 +102,7 @@
     "$dir_pw_stream:tests",
     "$dir_pw_string:tests",
     "$dir_pw_tokenizer:tests",
+    "$dir_pw_trace:tests",
     "$dir_pw_unit_test:tests",
     "$dir_pw_varint:tests",
   ]
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5670dba..e1c6392 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,5 +38,6 @@
 add_subdirectory(pw_sys_io)
 add_subdirectory(pw_sys_io_stdio)
 add_subdirectory(pw_tokenizer)
+add_subdirectory(pw_trace)
 add_subdirectory(pw_unit_test)
 add_subdirectory(pw_varint)
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index ca78872..a5ce2bb 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -95,6 +95,7 @@
     "$dir_pw_target_runner:docs",
     "$dir_pw_tokenizer:docs",
     "$dir_pw_toolchain:docs",
+    "$dir_pw_trace:docs",
     "$dir_pw_unit_test:docs",
     "$dir_pw_varint:docs",
     "$dir_pw_watch:docs",
diff --git a/modules.gni b/modules.gni
index 16988a5..48b7338 100644
--- a/modules.gni
+++ b/modules.gni
@@ -58,6 +58,7 @@
 dir_pw_target_runner = "$dir_pigweed/pw_target_runner"
 dir_pw_tokenizer = "$dir_pigweed/pw_tokenizer"
 dir_pw_toolchain = "$dir_pigweed/pw_toolchain"
+dir_pw_trace = "$dir_pigweed/pw_trace"
 dir_pw_unit_test = "$dir_pigweed/pw_unit_test"
 dir_pw_varint = "$dir_pigweed/pw_varint"
 dir_pw_watch = "$dir_pigweed/pw_watch"
diff --git a/pw_preprocessor/macro_arg_count_test.cc b/pw_preprocessor/macro_arg_count_test.cc
index fe985c2..0ce8831 100644
--- a/pw_preprocessor/macro_arg_count_test.cc
+++ b/pw_preprocessor/macro_arg_count_test.cc
@@ -251,5 +251,17 @@
   // clang-format on
 }
 
+TEST(DelegateByArgCount, WithoutAndWithoutArguments) {
+#define TEST_SUM0() (0)
+#define TEST_SUM1(a) (a)
+#define TEST_SUM2(a, b) ((a) + (b))
+#define TEST_SUM3(a, b, c) ((a) + (b) + (c))
+
+  static_assert(PW_DELEGATE_BY_ARG_COUNT(TEST_SUM) == 0);
+  static_assert(PW_DELEGATE_BY_ARG_COUNT(TEST_SUM, 5) == 5);
+  static_assert(PW_DELEGATE_BY_ARG_COUNT(TEST_SUM, 1, 2) == 3);
+  static_assert(PW_DELEGATE_BY_ARG_COUNT(TEST_SUM, 1, 2, 3) == 6);
+}
+
 }  // namespace
 }  // namespace pw
diff --git a/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h b/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
index cca6238..163eb47 100644
--- a/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
+++ b/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
@@ -136,3 +136,22 @@
 #define _PW_COMMA_ARGS_X(has_args, ...) _PW_COMMA_ARGS_##has_args(__VA_ARGS__)
 #define _PW_COMMA_ARGS_0(...)                // no args, no comma
 #define _PW_COMMA_ARGS_1(...) , __VA_ARGS__  // comma, followed by args
+
+// Allows calling a different function-like macro based on the number of
+// arguments.  For example:
+//
+//   #define ARG_PRINT(...)  PW_DELEGATE_BY_ARG_COUNT(_ARG_PRINT, __VA_ARGS__)
+//   #define _ARG_PRINT1(a)        LOG_INFO("1 arg: %s", a)
+//   #define _ARG_PRINT2(a, b)     LOG_INFO("2 args: %s, %s", a, b)
+//   #define _ARG_PRINT3(a, b, c)  LOG_INFO("3 args: %s, %s, %s", a, b, c)
+//
+// This can the be called in code:
+//    ARG_PRINT("a");            // Outputs: 1 arg: a
+//    ARG_PRINT("a", "b");       // Outputs: 2 arg: a, b
+//    ARG_PRINT("a", "b", "c");  // Outputs: 3 arg: a, b, c
+//
+#define PW_DELEGATE_BY_ARG_COUNT(func, ...) \
+  _PW_DELEGATE_BY_ARG_COUNT(func, PW_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__)
+#define _PW_DELEGATE_BY_ARG_COUNT_EXPANDED(name, n) name##n
+#define _PW_DELEGATE_BY_ARG_COUNT(name, n) \
+  _PW_DELEGATE_BY_ARG_COUNT_EXPANDED(name, n)
diff --git a/pw_trace/BUILD b/pw_trace/BUILD
new file mode 100644
index 0000000..d84f847
--- /dev/null
+++ b/pw_trace/BUILD
@@ -0,0 +1,84 @@
+# Copyright 2020 The Pigweed Authors
+#
+# 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
+#
+#     https://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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+    "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+# TODO(pwbug/101): Need to add support for facades/backends to Bazel.
+
+pw_cc_library(
+    name = "facade",
+    hdrs = [
+        "public/pw_trace/trace.h",
+        "public/pw_trace/internal/trace_internal.h",
+    ],
+    includes = ["public"],
+    deps = [
+        "//pw_preprocessor",
+    ],
+)
+
+pw_cc_library(
+    name = "pw_trace",
+    deps = [
+        ":facade",
+    ],
+)
+
+pw_cc_library(
+    name = "backend",
+    deps = [],
+)
+
+pw_cc_test(
+    name = "trace_backend_compile_test",
+    srcs = [
+        "trace_backend_compile_test.cc",
+        "trace_backend_compile_test.c",
+    ],
+    deps = [
+        ":backend",
+        ":facade",
+        ":pw_trace",
+        "//pw_preprocessor",
+        "//pw_unit_test",
+    ],
+)
+
+pw_cc_test(
+    name = "trace_facade_test",
+    srcs = [
+        "trace_facade_test.cc",
+        "pw_trace_test/fake_backend.h",
+        "pw_trace_test/public_overrides/pw_trace_backend/trace_backend.h",
+    ],
+    includes = [
+        "pw_trace_test",
+        "pw_trace_test/public_overrides"
+    ],
+    deps = [
+        ":backend",
+        ":facade",
+        ":pw_trace",
+        "//pw_preprocessor",
+        "//pw_unit_test",
+    ],
+)
diff --git a/pw_trace/BUILD.gn b/pw_trace/BUILD.gn
new file mode 100644
index 0000000..a6d873b
--- /dev/null
+++ b/pw_trace/BUILD.gn
@@ -0,0 +1,71 @@
+# Copyright 2020 The Pigweed Authors
+#
+# 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
+#
+#     https://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.
+
+import("$dir_pw_build/facade.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+pw_facade("pw_trace") {
+  backend = dir_pw_trace_backend
+  public_configs = [ ":default_config" ]
+  public = [
+    "public/pw_trace/internal/trace_internal.h",
+    "public/pw_trace/trace.h",
+  ]
+  deps = [ dir_pw_preprocessor ]
+}
+
+pw_test_group("tests") {
+  tests = [ ":trace_facade_test" ]
+  if (dir_pw_trace_backend != "") {
+    tests += [ ":trace_backend_compile_test" ]
+  }
+}
+
+pw_test("trace_facade_test") {
+  configs = [ ":default_config" ]
+  sources = [ "trace_facade_test.cc" ]
+  public = [
+    "public/pw_trace/internal/trace_internal.h",
+    "public/pw_trace/trace.h",
+    "pw_trace_test/fake_backend.h",
+    "pw_trace_test/public_overrides/pw_trace_backend/trace_backend.h",
+  ]
+  include_dirs = [
+    "pw_trace_test",
+    "pw_trace_test/public_overrides",
+  ]
+}
+
+pw_test("trace_backend_compile_test") {
+  enable_if = dir_pw_trace_backend != ""
+
+  deps = [
+    ":pw_trace",
+    dir_pw_trace_backend,
+  ]
+
+  sources = [
+    "trace_backend_compile_test.c",
+    "trace_backend_compile_test.cc",
+  ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_trace/CMakeLists.txt b/pw_trace/CMakeLists.txt
new file mode 100644
index 0000000..50f67ba
--- /dev/null
+++ b/pw_trace/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright 2020 The Pigweed Authors
+#
+# 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
+#
+#     https://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.
+
+pw_add_facade(pw_trace
+  PUBLIC_DEPS
+    pw_preprocessor
+)
diff --git a/pw_trace/README.md b/pw_trace/README.md
new file mode 100644
index 0000000..6424490
--- /dev/null
+++ b/pw_trace/README.md
@@ -0,0 +1,3 @@
+This directory contains the trace facade for pigweed.
+
+This code is experimental and unfinished.
\ No newline at end of file
diff --git a/pw_trace/docs.rst b/pw_trace/docs.rst
new file mode 100644
index 0000000..37e0234
--- /dev/null
+++ b/pw_trace/docs.rst
@@ -0,0 +1,249 @@
+.. _chapter-pw-trace:
+
+.. default-domain:: cpp
+
+.. highlight:: cpp
+
+========
+pw_trace
+========
+Pigweed's tracing module provides facilities for applications to trace
+information about the execution of their application. The module is split into
+two components:
+
+1. The facade (this module) which is only a macro interface layer
+2. The backend, provided elsewhere, implements the low level tracing.
+
+------
+Status
+------
+This module is currently in development, and is therefore still undergoing
+significant changes.
+
+Future work will add:
+
+1. A Python library to generate trace files which can be viewed. (pwbug/205)
+2. Add more examples with sample output. (pwbug/207)
+3. Implement a trace backend module. (pwbug/260)
+
+--------
+Overview
+--------
+Traces provide a useful view which shows the flow of events through a system,
+and can greatly assist in understanding complex software problems. These traces
+can either show what tasks are running at any given time, or use added code
+(similar to logging), to help annotate specific interesting flows.
+
+Fundamentally, tracing is similar to logging in that it provides the developer
+with a view into what the system was doing at a particular time. The fundamental
+difference between logs and tracing is that logs present information as ad-hoc
+human-readable strings and are better suited to providing the current state of
+the system. Instead, traces capture sequences of events with precise timestamps,
+and are therefore useful at understanding the flow of events in the system over
+time.
+
+Compatibility
+-------------
+Most of the facade is compatible with C and C++, the only exception to this is
+the Scope and Function tracing macros which are convenience wrappers only
+available in C++.
+
+Dependencies
+-------------
+``pw_preprocessor``
+
+Example
+-------
+
+.. code-block:: cpp
+
+  #define PW_TRACE_MODULE_NAME "Input"
+  #include "pw_trace/trace.h"
+
+  void SendButton() {
+    PW_TRACE_FUNCTION()
+    // do something
+  }
+
+  void InputLoop() {
+    while(1) {
+      auto event = WaitNewInputEvent()
+      TRACE_SCOPE("Handle Event");  // measure until loop finished
+      if (event == kNewButton){
+        SendButton();
+        PW_TRACE_END("button");  // Trace event was started in ButtonIsr
+      } else {
+        PW_TRACE_INSTANT("Unknown event");
+      }
+    }
+  }
+
+  void ButtonIsr() {
+    PW_TRACE_START("button");
+    SendNewInputEvent(kNewButton);
+  }
+
+
+------------
+Trace macros
+------------
+
+The ``pw_trace`` public API provides three basic trace events:
+
+- ``PW_TRACE_INSTANT`` - This is used to trace an instant event, which has no
+  duration.
+- ``PW_TRACE_START`` & ``PW_TRACE_END`` - Trace 'start' and 'end' events are
+  paired together to measure the duration of an event.
+
+These trace event macros all have the same arguments:
+
+- *label* - Each of these trace events must have a label, which is a string
+  describing the event. In addition to the required label, each of these traces
+  can optionally provide a group label and trace id.
+- *group_label* - The *optional* group label is used if many traces are all
+  measuring the same thing and should be grouped together. This information will
+  be used when visualizing the trace to ensure the information appears together.
+- *trace_id* - The *optional* trace id is similar to the group_id, but instead
+  groups events using a runtime value. This can be used if multiple trace flow
+  might happen at the same time and need to be grouped together.
+  For example, this could be used to trace data packets flowing through the
+  system; when a new sample might arrive before the previous packet is finished
+  processing. This would result in two start events occurring before the end
+  event. By providing a trace id with a different value for each packet, these
+  can be separated when decoding.
+
+.. tip::
+
+  All of these arguments must be the same for a *start* and *end* pair.
+
+This results in 9 different trace calls:
+
+.. cpp:function:: PW_TRACE_INSTANT(label)
+.. cpp:function:: PW_TRACE_INSTANT(label, group_label)
+.. cpp:function:: PW_TRACE_INSTANT(label, group_label, trace_id)
+.. cpp:function:: PW_TRACE_START(label)
+.. cpp:function:: PW_TRACE_START(label, group_label)
+.. cpp:function:: PW_TRACE_START(label, group_label, trace_id)
+.. cpp:function:: PW_TRACE_END(label)
+.. cpp:function:: PW_TRACE_END(label, group_label)
+.. cpp:function:: PW_TRACE_END(label, group_label, trace_id)
+
+Modules
+---------
+In addition to these arguments, traces can be grouped into modules similar to
+logging. This is done by redefining the ``PW_TRACE_MODULE_NAME``.
+
+Flags
+-----
+Each trace event also has a flags field which can be used to provide additional
+compile time trace information. Each trace macro has a matching macro which
+allows specifying the flag:
+
+.. cpp:function:: PW_TRACE_INSTANT_FLAG(flag, label)
+.. cpp:function:: PW_TRACE_INSTANT_FLAG(flag, label, group_label)
+.. cpp:function:: PW_TRACE_INSTANT_FLAG(flag, label, group_label, trace_id)
+.. cpp:function:: PW_TRACE_START_FLAG(flag, label)
+.. cpp:function:: PW_TRACE_START_FLAG(flag, label, group_label)
+.. cpp:function:: PW_TRACE_START_FLAG(flag, label, group_label, trace_id)
+.. cpp:function:: PW_TRACE_END_FLAG(flag, label)
+.. cpp:function:: PW_TRACE_END_FLAG(flag, label, group_label)
+.. cpp:function:: PW_TRACE_END_FLAG(flag, label, group_label, trace_id)
+
+When not specified the flag uses the value of the macro ``PW_TRACE_FLAGS``.
+
+Data
+----
+Each macro also has a variant which allows appending additional data:
+
+.. cpp:function:: PW_TRACE_INSTANT_DATA(label, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_INSTANT_DATA(\
+   label, group_label, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_INSTANT_DATA(\
+   label, group_label, trace_id, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_START_DATA(label, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_START_DATA(\
+   label, group_label, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_START_DATA(\
+   label, group_label, trace_id, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_END_DATA(label, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_END_DATA(\
+   label, group_label, data_format_string, data, size)
+.. cpp:function:: PW_TRACE_END_DATA(\
+   label, group_label, trace_id, data_format_string, data, size)
+
+These macros require 3 additional arguments:
+
+- *data_format_string* - A string which is used by the decoder to identify the
+  data. This could for example either be printf style tokens, python struct
+  packed fmt string or a custom label recognized by the decoder.
+- *data* - A pointer to a buffer of arbitrary caller-provided data (void*).
+- *size* - The size of the data (size_t).
+
+.. tip::
+
+  It is ok for only one event of a start/end pair to contain data, as long the
+  *label*, *group_label*, and *trace_id*, are all the same.
+
+C++ Only Traces
+---------------
+Scope API measures durations until the object loses scope. This can for
+example, provide a convenient method of tracing functions or loops.
+
+.. cpp:function:: PW_TRACE_SCOPE(label)
+.. cpp:function:: PW_TRACE_SCOPE(label, group_label)
+
+Function API measures durations until the function returns. This is the only
+macro which does not require a *label*, and instead uses the function name as the
+label. It still can optionally be provided a *group_id*.
+
+.. cpp:function:: PW_TRACE_FUNCTION()
+.. cpp:function:: PW_TRACE_FUNCTION(group_label)
+
+-------------
+Backend API
+-------------
+Each of the trace event macros get sent to one of two macros which are
+implemented by the backend:
+
+.. cpp:function:: PW_TRACE(event_type, flags, label, group_label, trace_id)
+.. cpp:function:: PW_TRACE_DATA(event_type, flags, label, group_label, \
+   trace_id, data_format_string, data, size)
+
+The ``event_type`` value will be whatever the backend defined for that specific
+trace type using the macros defined below.
+
+The backend can optionally not define ``PW_TRACE_DATA`` to have those traces
+disabled.
+
+Trace types
+-----------
+Although there are only 3 basic trace types, each has 3 variants:
+
+- Label only
+- Label and group
+- Label, group, and trace_id
+
+This combination creates 9 different trace event types:
+
+- *PW_TRACE_TYPE_INSTANT*: Instant trace, with only a label.
+- *PW_TRACE_TYPE_DURATION_START*: Start trace, with only a label.
+- *PW_TRACE_TYPE_DURATION_END*: End trace, with only a label.
+- *PW_TRACE_TYPE_INSTANT_GROUP*: Instant trace, with a label and a group.
+- *PW_TRACE_TYPE_DURATION_GROUP_START*: Start trace, with a label and a group.
+- *PW_TRACE_TYPE_DURATION_GROUP_END*: End trace, with a label and a group.
+- *PW_TRACE_TYPE_ASYNC_INSTANT*: Instant trace, with label, group, and trace_id
+- *PW_TRACE_TYPE_ASYNC_START*: Start trace, with label, group, and trace_id.
+- *PW_TRACE_TYPE_ASYNC_END*: End trace, with label, group, and trace_id.
+
+The backend must define these macros to have them enabled. If any are left
+undefined, any traces of that type are removed.
+
+Defaults
+--------
+The backend can use these macros to change what the default value is if not
+provided.
+
+- *PW_TRACE_FLAGS_DEFAULT*: Default value if no flags are provided.
+- *PW_TRACE_TRACE_ID_DEFAULT*: Default value if not trace_id provided.
+- *PW_TRACE_GROUP_LABEL_DEFAULT*: Default value if not group_label provided.
+
diff --git a/pw_trace/public/pw_trace/internal/trace_internal.h b/pw_trace/public/pw_trace/internal/trace_internal.h
new file mode 100644
index 0000000..ad77f57
--- /dev/null
+++ b/pw_trace/public/pw_trace/internal/trace_internal.h
@@ -0,0 +1,293 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+//==============================================================================
+// This file contains the backend hooks and implementation details for trace.
+
+#pragma once
+
+#include "pw_preprocessor/macro_arg_count.h"
+#include "pw_trace_backend/trace_backend.h"
+
+// Default: Flag value if none set
+#ifndef PW_TRACE_FLAGS_DEFAULT
+#define PW_TRACE_FLAGS_DEFAULT 0
+#endif  // PW_TRACE_FLAGS_DEFAULT
+
+// Default: PW_TRACE_TRACE_ID_DEFAULT
+#ifndef PW_TRACE_TRACE_ID_DEFAULT
+#define PW_TRACE_TRACE_ID_DEFAULT 0
+#endif  // PW_TRACE_TRACE_ID_DEFAULT
+
+// Default: PW_TRACE_GROUP_LABEL_DEFAULT
+#ifndef PW_TRACE_GROUP_LABEL_DEFAULT
+#define PW_TRACE_GROUP_LABEL_DEFAULT ""
+#endif  // PW_TRACE_GROUP_LABEL_DEFAULT
+
+// These macros can be used to determine if a trace type contrains span or group
+// label
+#ifndef PW_TRACE_HAS_TRACE_ID
+#define PW_TRACE_HAS_TRACE_ID(TRACE_TYPE)         \
+  ((TRACE_TYPE) == PW_TRACE_TYPE_ASYNC_START ||   \
+   (TRACE_TYPE) == PW_TRACE_TYPE_ASYNC_INSTANT || \
+   (TRACE_TYPE) == PW_TRACE_TYPE_ASYNC_END)
+#endif  // PW_TRACE_HAS_TRACE_ID
+#ifndef PW_TRACE_HAS_GROUP_LABEL
+#define PW_TRACE_HAS_GROUP_LABEL(TRACE_TYPE) (false)
+#endif  // PW_TRACE_HAS_GROUP_LABEL
+
+// Default: behaviour for unimplemented trace event types
+#ifndef _PW_TRACE_DISABLED
+#define _PW_TRACE_DISABLED(...) \
+  if (0) {                      \
+  }
+#endif  // _PW_TRACE_DISABLED
+
+#ifdef PW_TRACE_TYPE_INSTANT  // Disabled if backend doesn't define this
+#define _PW_TRACE_INSTANT_ARGS2(flag, label) \
+  PW_TRACE(PW_TRACE_TYPE_INSTANT,            \
+           flag,                             \
+           label,                            \
+           PW_TRACE_GROUP_LABEL_DEFAULT,     \
+           PW_TRACE_TRACE_ID_DEFAULT)
+#else  // PW_TRACE_TYPE_INSTANT
+#define _PW_TRACE_INSTANT_ARGS2(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_INSTANT
+
+#ifdef PW_TRACE_TYPE_INSTANT_GROUP  // Disabled if backend doesn't define this
+#define _PW_TRACE_INSTANT_ARGS3(flag, label, group) \
+  PW_TRACE(PW_TRACE_TYPE_INSTANT_GROUP,             \
+           flag,                                    \
+           label,                                   \
+           group,                                   \
+           PW_TRACE_TRACE_ID_DEFAULT)
+#else  // PW_TRACE_TYPE_INSTANT_GROUP
+#define _PW_TRACE_INSTANT_ARGS3(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_INSTANT_GROUP
+
+#ifdef PW_TRACE_TYPE_ASYNC_INSTANT  // Disabled if backend doesn't define this
+#define _PW_TRACE_INSTANT_ARGS4(flag, label, group, trace_id) \
+  PW_TRACE(PW_TRACE_TYPE_ASYNC_INSTANT, flag, label, group, trace_id)
+#else  // PW_TRACE_TYPE_ASYNC_INSTANT
+#define _PW_TRACE_INSTANT_ARGS4(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_ASYNC_INSTANT
+
+#ifdef PW_TRACE_TYPE_DURATION_START  // Disabled if backend doesn't define this
+#define _PW_TRACE_START_ARGS2(flag, label) \
+  PW_TRACE(PW_TRACE_TYPE_DURATION_START,   \
+           flag,                           \
+           label,                          \
+           PW_TRACE_GROUP_LABEL_DEFAULT,   \
+           PW_TRACE_TRACE_ID_DEFAULT)
+#else  // PW_TRACE_TYPE_DURATION_START
+#define _PW_TRACE_START_ARGS2(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_DURATION_START
+
+#ifdef PW_TRACE_TYPE_DURATION_GROUP_START  // Disabled if backend doesn't define
+                                           // this
+#define _PW_TRACE_START_ARGS3(flag, label, group) \
+  PW_TRACE(PW_TRACE_TYPE_DURATION_GROUP_START,    \
+           flag,                                  \
+           label,                                 \
+           group,                                 \
+           PW_TRACE_TRACE_ID_DEFAULT)
+#else  // PW_TRACE_TYPE_DURATION_GROUP_START
+#define _PW_TRACE_START_ARGS3(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_DURATION_GROUP_START
+
+#ifdef PW_TRACE_TYPE_ASYNC_START  // Disabled if backend doesn't define this
+#define _PW_TRACE_START_ARGS4(flag, label, group, trace_id) \
+  PW_TRACE(PW_TRACE_TYPE_ASYNC_START, flag, label, group, trace_id)
+#else  // PW_TRACE_TYPE_ASYNC_START
+#define _PW_TRACE_START_ARGS4(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_ASYNC_START
+
+#ifdef PW_TRACE_TYPE_DURATION_END  // Disabled if backend doesn't define this
+#define _PW_TRACE_END_ARGS2(flag, label) \
+  PW_TRACE(PW_TRACE_TYPE_DURATION_END,   \
+           flag,                         \
+           label,                        \
+           PW_TRACE_GROUP_LABEL_DEFAULT, \
+           PW_TRACE_TRACE_ID_DEFAULT)
+#else  // PW_TRACE_TYPE_DURATION_END
+#define _PW_TRACE_END_ARGS2(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_DURATION_END
+
+#ifdef PW_TRACE_TYPE_DURATION_GROUP_END  // Disabled if backend doesn't define
+                                         // this
+#define _PW_TRACE_END_ARGS3(flag, label, group) \
+  PW_TRACE(PW_TRACE_TYPE_DURATION_GROUP_END,    \
+           flag,                                \
+           label,                               \
+           group,                               \
+           PW_TRACE_TRACE_ID_DEFAULT)
+#else  // PW_TRACE_TYPE_DURATION_GROUP_END
+#define _PW_TRACE_END_ARGS3(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_DURATION_GROUP_END
+
+#ifdef PW_TRACE_TYPE_ASYNC_END  // Disabled if backend doesn't define this
+#define _PW_TRACE_END_ARGS4(flag, label, group, trace_id) \
+  PW_TRACE(PW_TRACE_TYPE_ASYNC_END, flag, label, group, trace_id)
+#else  // PW_TRACE_TYPE_ASYNC_END
+#define _PW_TRACE_END_ARGS4(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // PW_TRACE_TYPE_ASYNC_END
+
+#ifndef _PW_TRACE_SCOPE_OBJECT
+#define _PW_TRACE_SCOPE_OBJECT(object_name, flag, ...)                 \
+  class object_name {                                                  \
+   public:                                                             \
+    object_name(const char* label) : label_(label) {                   \
+      PW_TRACE_START_FLAG((flag), (label_)PW_COMMA_ARGS(__VA_ARGS__)); \
+    }                                                                  \
+    ~object_name() {                                                   \
+      PW_TRACE_END_FLAG((flag), (label_)PW_COMMA_ARGS(__VA_ARGS__));   \
+    }                                                                  \
+                                                                       \
+   private:                                                            \
+    const char* label_;                                                \
+  }
+#endif  // _PW_TRACE_SCOPE_OBJECT
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_INSTANT)
+#define _PW_TRACE_INSTANT_DATA_ARGS5(            \
+    flag, label, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_INSTANT,           \
+                flag,                            \
+                label,                           \
+                PW_TRACE_GROUP_LABEL_DEFAULT,    \
+                PW_TRACE_TRACE_ID_DEFAULT,       \
+                data_format_string,              \
+                data,                            \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_INSTANT)
+#define _PW_TRACE_INSTANT_DATA_ARGS5(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_INSTANT)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_INSTANT_GROUP)
+#define _PW_TRACE_INSTANT_DATA_ARGS6(                   \
+    flag, label, group, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_INSTANT_GROUP,            \
+                flag,                                   \
+                label,                                  \
+                group,                                  \
+                PW_TRACE_TRACE_ID_DEFAULT,              \
+                data_format_string,                     \
+                data,                                   \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_INSTANT_GROUP)
+#define _PW_TRACE_INSTANT_DATA_ARGS6(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_INSTANT_GROUP)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_INSTANT)
+#define _PW_TRACE_INSTANT_DATA_ARGS7(                             \
+    flag, label, group, trace_id, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_ASYNC_INSTANT,                      \
+                flag,                                             \
+                label,                                            \
+                group,                                            \
+                trace_id,                                         \
+                data_format_string,                               \
+                data,                                             \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_INSTANT)
+#define _PW_TRACE_INSTANT_DATA_ARGS7(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_INSTANT)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+#define _PW_TRACE_START_DATA_ARGS5(              \
+    flag, label, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_DURATION_START,    \
+                flag,                            \
+                label,                           \
+                PW_TRACE_GROUP_LABEL_DEFAULT,    \
+                PW_TRACE_TRACE_ID_DEFAULT,       \
+                data_format_string,              \
+                data,                            \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+#define _PW_TRACE_START_DATA_ARGS5(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_GROUP_START)
+#define _PW_TRACE_START_DATA_ARGS6(                     \
+    flag, label, group, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_DURATION_GROUP_START,     \
+                flag,                                   \
+                label,                                  \
+                group,                                  \
+                PW_TRACE_TRACE_ID_DEFAULT,              \
+                data_format_string,                     \
+                data,                                   \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+#define _PW_TRACE_START_DATA_ARGS6(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_START)
+#define _PW_TRACE_START_DATA_ARGS7(                               \
+    flag, label, group, trace_id, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_ASYNC_START,                        \
+                flag,                                             \
+                label,                                            \
+                group,                                            \
+                trace_id,                                         \
+                data_format_string,                               \
+                data,                                             \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_START)
+#define _PW_TRACE_START_DATA_ARGS7(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_START)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_END)
+#define _PW_TRACE_END_DATA_ARGS5(flag, label, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_DURATION_END,                                   \
+                flag,                                                         \
+                label,                                                        \
+                PW_TRACE_GROUP_LABEL_DEFAULT,                                 \
+                PW_TRACE_TRACE_ID_DEFAULT,                                    \
+                data_format_string,                                           \
+                data,                                                         \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+#define _PW_TRACE_END_DATA_ARGS5(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_START)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_GROUP_END)
+#define _PW_TRACE_END_DATA_ARGS6(                       \
+    flag, label, group, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_DURATION_GROUP_END,       \
+                flag,                                   \
+                label,                                  \
+                group,                                  \
+                PW_TRACE_TRACE_ID_DEFAULT,              \
+                data_format_string,                     \
+                data,                                   \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_GROUP_END)
+#define _PW_TRACE_END_DATA_ARGS6(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_DURATION_GROUP_END)
+
+#if defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_END)
+#define _PW_TRACE_END_DATA_ARGS7(                                 \
+    flag, label, group, trace_id, data_format_string, data, size) \
+  PW_TRACE_DATA(PW_TRACE_TYPE_ASYNC_END,                          \
+                flag,                                             \
+                label,                                            \
+                group,                                            \
+                trace_id,                                         \
+                data_format_string,                               \
+                data,                                             \
+                size)
+#else  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_END)
+#define _PW_TRACE_END_DATA_ARGS7(...) _PW_TRACE_DISABLED(__VA_ARGS__)
+#endif  // defined(PW_TRACE_DATA) && defined(PW_TRACE_TYPE_ASYNC_END)
diff --git a/pw_trace/public/pw_trace/trace.h b/pw_trace/public/pw_trace/trace.h
new file mode 100644
index 0000000..f1a4c9a
--- /dev/null
+++ b/pw_trace/public/pw_trace/trace.h
@@ -0,0 +1,413 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+//==============================================================================
+//
+// This file describes Pigweed's public user-facing tracing API.
+//
+// THIS PUBLIC API IS NOT STABLE OR COMPLETE!
+//
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "pw_preprocessor/util.h"
+#include "pw_trace/internal/trace_internal.h"
+
+// Backend defines PW_TRACE in the form:
+// #define PW_TRACE(event_type, flags, label, group_label, trace_id)
+
+// trace_backend.h must ultimately resolve to a header that implements the
+// macros required by the tracing facade, as described below.
+//
+// Inputs: Macros the downstream user provides to control the tracing system:
+//
+//   PW_TRACE_MODULE_NAME
+//     - The module name the backend should use
+//
+// Outputs: Macros trace_backend.h is expected to provide:
+//
+//   PW_TRACE(event_type, flags, label, group_label, trace_id)
+//   PW_TRACE_DATA(event_type, flags, label, group_label, trace_id,
+//                 data_format_string, data, data_size)
+//      - Implementing PW_TRACE_DATA is optional. If not defined, all data
+//        traces will be removed.
+//
+//   event_type will pass the macro value which is defined by the backend when
+//   enabling the trace type.
+//
+// Enabling traces: The backend can define these macros to enable the different
+//                  trace types. If not defined those traces are removed.
+//
+//   PW_TRACE_TYPE_INSTANT: Instant trace, with only a label.
+//   PW_TRACE_TYPE_INSTANT_GROUP: Instant trace, with a label and a group.
+//   PW_TRACE_TYPE_DURATION_START: Start trace, with only a label.
+//   PW_TRACE_TYPE_DURATION_END: End trace, with only a label.
+//   PW_TRACE_TYPE_DURATION_GROUP_START: Start trace, with a label and a group.
+//   PW_TRACE_TYPE_DURATION_GROUP_END: End trace, with a label and a group.
+//   PW_TRACE_TYPE_ASYNC_START: Start trace, with label, group, and trace_id.
+//   PW_TRACE_TYPE_ASYNC_INSTANT: Instant trace, with label, group, and trace_id
+//   PW_TRACE_TYPE_ASYNC_END: End trace, with label, group, and trace_id.
+//
+// Defaults: The backend can use the macros to change what the default value is
+//           if not provided.
+//
+//   PW_TRACE_FLAGS_DEFAULT: Default value if no flags are provided.
+//   PW_TRACE_TRACE_ID_DEFAULT: Default value if not trace_id provided.
+//   PW_TRACE_GROUP_LABEL_DEFAULT: Default value if not group_label provided.
+
+#include "pw_trace_backend/trace_backend.h"
+
+// Default: Module name
+#ifndef PW_TRACE_MODULE_NAME
+#define PW_TRACE_MODULE_NAME ""
+#endif  // PW_TRACE_MODULE_NAME
+
+// Default: Flags values currently set if not provided
+#ifndef PW_TRACE_FLAGS
+#define PW_TRACE_FLAGS PW_TRACE_FLAGS_DEFAULT
+#endif  // PW_TRACE_FLAGS
+
+// PW_TRACE_INSTANT(label)
+// PW_TRACE_INSTANT(label, group)
+// PW_TRACE_INSTANT(label, group, trace_id)
+//
+// Used to trace an instantaneous event in code.
+//
+// Example usage:
+//     PW_TRACE_INSTANT("HERE");
+//
+// Arguments:
+//     label: A string literal which desribes the trace
+//     group <optional>: A string literal which groups this trace with others in
+//                       the same module and group.
+//     trace_id <optional>: A runtime uint32_t which groups this trace with
+//                          others with the same module group and trace_id.
+//                          Every trace with a trace_id must also have a group.
+#define PW_TRACE_INSTANT(...) PW_TRACE_INSTANT_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_INSTANT_FLAG(flag, label)
+// PW_TRACE_INSTANT_FLAG(flag, label, group)
+// PW_TRACE_INSTANT_FLAG(flag, label, group, trace_id)
+//
+// These macros mirror PW_TRACE_INSTANT but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+#define PW_TRACE_INSTANT_FLAG(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_TRACE_INSTANT_ARGS, __VA_ARGS__)
+
+// PW_TRACE_INSTANT_DATA(label, data_format_string, data, size)
+// PW_TRACE_INSTANT_DATA(label, group, data_format_string, data, size)
+// PW_TRACE_INSTANT_DATA(label, group, trace_id, data_format_string, data, size)
+//
+// These macros mirror PW_TRACE_INSTANT but introduce arguments to specify a
+// user-supplied data buffer to append to the trace event.
+//
+// Arguments:
+//     data_format_string: A string which is used by the decoder to identify the
+//                        data. This could for example be either be printf style
+//                        tokens, python struct packed fmt string or a custom
+//                        label recognized by the decoder.
+//     data: A pointer to a buffer of arbitrary caller-provided data (void*).
+//     size: The size of the data (size_t).
+#define PW_TRACE_INSTANT_DATA(...) \
+  PW_TRACE_INSTANT_DATA_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_INSTANT_DATA_FLAG(flag, label, data_format_string, data, size)
+// PW_TRACE_INSTANT_DATA_FLAG(flag,
+//                            label,
+//                            group,
+//                            data_format_string,
+//                            data,
+//                            size)
+// PW_TRACE_INSTANT_DATA_FLAG(flag,
+//                            label,
+//                            group,
+//                            trace_id,
+//                            data_format_string,
+//                            data,
+//                            size)
+//
+// These macros mirror PW_TRACE_INSTANT_DATA but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+//
+// Arguments:
+//     data_format_string: A string which is used by the decoder to identify the
+//                        data. This could for example be either be printf style
+//                        tokens, python struct packed fmt string or a custom
+//                        label recognized by the decoder.
+//     data: A pointer to a buffer of arbitrary caller-provided data (void*).
+//     size: The size of the data (size_t).
+#define PW_TRACE_INSTANT_DATA_FLAG(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_TRACE_INSTANT_DATA_ARGS, __VA_ARGS__)
+
+// PW_TRACE_START(label)
+// PW_TRACE_START(label, group)
+// PW_TRACE_START(label, group, trace_id)
+//
+// Used to start tracing an event, should be paired with an PW_TRACE_END (or
+// PW_TRACE_END_DATA) with the same module/label/group/trace_id.
+//
+// Example usage:
+//    PW_TRACE_START("label");
+//    .. Do something ..
+//    PW_TRACE_END("label");
+//
+// Arguments:
+//     label: A string literal which desribes the trace
+//     group <optional>: A string literal which groups this trace with others in
+//                       the same module and group.
+//     trace_id <optional>: A runtime uint32_t which groups this trace with
+//                          others with the same module group and trace_id.
+//                          Every trace with a trace_id must also have a group.
+#define PW_TRACE_START(...) PW_TRACE_START_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_START_FLAG(flag, label)
+// PW_TRACE_START_FLAG(flag, label, group)
+// PW_TRACE_START_FLAG(flag, label, group, trace_id)
+//
+// These macros mirror PW_TRACE_START but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+#define PW_TRACE_START_FLAG(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_TRACE_START_ARGS, __VA_ARGS__)
+
+// PW_TRACE_START_DATA(label, data_format_string, data, size)
+// PW_TRACE_START_DATA(label, group, data_format_string, data, size)
+// PW_TRACE_START_DATA(flag,
+//                     label,
+//                     group,
+//                     trace_id,
+//                     data_format_string,
+//                     data,
+//                     size)
+//
+// These macros mirror PW_TRACE_START but introduce arguments to specify a
+// user-supplied data buffer to append to the trace event.
+//
+// NOTE: A trace duration start/end can be combined with a duration data
+// start/end, to include data at only one of the trace points and not the other.
+//
+// Arguments:
+//     data_format_string: A string which is used by the decoder to identify the
+//                        data. This could for example be either be printf style
+//                        tokens, python struct packed fmt string or a custom
+//                        label recognized by the decoder.
+//     data: A pointer to a buffer of arbitrary caller-provided data (void*).
+//     size: The size of the data (size_t).
+#define PW_TRACE_START_DATA(...) \
+  PW_TRACE_START_DATA_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_START_DATA_FLAG(flag, label, data_format_string, data, size)
+// PW_TRACE_START_DATA_FLAG(flag, label, group, data_format_string, data, size)
+// PW_TRACE_START_DATA_FLAG(flag,
+//                          label,
+//                          group,
+//                          trace_id,
+//                          data_format_string,
+//                          data,
+//                          size)
+//
+// These macros mirror PW_TRACE_START_DATA but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+//
+// Arguments:
+//     data_format_string: A string which is used by the decoder to identify the
+//                        data. This could for example be either be printf style
+//                        tokens, python struct packed fmt string or a custom
+//                        label recognized by the decoder.
+//     data: A pointer to a buffer of arbitrary caller-provided data (void*).
+//     size: The size of the data (size_t).
+#define PW_TRACE_START_DATA_FLAG(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_TRACE_START_DATA_ARGS, __VA_ARGS__)
+
+// PW_TRACE_END(label)
+// PW_TRACE_END(label, group)
+// PW_TRACE_END(label, group, trace_id)
+//
+// Used to start tracing an event, should be paired with an PW_TRACE_START (or
+// PW_TRACE_START_DATA) with the same module/label/group/trace_id.
+//
+// Example usage:
+//    PW_TRACE_START("label");
+//    .. Do something ..
+//    PW_TRACE_END("label");
+//
+// Arguments:
+//     label: A string literal which desribes the trace
+//     group <optional>: A string literal which groups this trace with others in
+//                       the same module and group.
+//     trace_id <optional>: A runtime uint32_t which groups this trace with
+//                          others with the same module group and trace_id.
+//                          Every trace with a trace_id must also have a group.
+#define PW_TRACE_END(...) PW_TRACE_END_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_END_FLAG(flag, label)
+// PW_TRACE_END_FLAG(flag, label, group)
+// PW_TRACE_END_FLAG(flag, label, group, trace_id)
+//
+// Is the same as PW_TRACE_END but uses the provided flag value instead of
+// PW_TRACE_FLAGS. The flag goes at the start, group and trace_id are still
+// optional.
+#define PW_TRACE_END_FLAG(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_TRACE_END_ARGS, __VA_ARGS__)
+
+// PW_TRACE_END_DATA(label, data_format_string, data, size)
+// PW_TRACE_END_DATA(label, group, data_format_string, data, size)
+// PW_TRACE_END_DATA(label, group, trace_id, data_format_string, data, size)
+//
+// These macros mirror PW_TRACE_END but introduce arguments to specify a
+// user-supplied data buffer to append to the trace event.
+//
+// NOTE: A trace duration start/end can be combined with a duration data
+// start/end, to include data at only one of the trace points and not the other.
+//
+// Arguments:
+//     data_format_string: A string which is used by the decoder to identify the
+//                        data. This could for example be either be printf style
+//                        tokens, python struct packed fmt string or a custom
+//                        label recognized by the decoder.
+//     data: A pointer to a buffer of arbitrary caller-provided data (void*).
+//     size: The size of the data (size_t).
+#define PW_TRACE_END_DATA(...) \
+  PW_TRACE_END_DATA_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_END_DATA_FLAG(flag, label, data_format_string, data, size)
+// PW_TRACE_END_DATA_FLAG(flag, label, group, data_format_string, data, size)
+// PW_TRACE_END_DATA_FLAG(flag,
+//                        label,
+//                        group,
+//                        trace_id,
+//                        data_format_string,
+//                        data,
+//                        size)
+//
+// These macros mirror PW_TRACE_END_DATA but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+//
+// Arguments:
+//     data_format_string: A string which is used by the decoder to identify the
+//                        data. This could for example be either be printf style
+//                        tokens, python struct packed fmt string or a custom
+//                        label recognized by the decoder.
+//     data: A pointer to a buffer of arbitrary caller-provided data (void*).
+//     size: The size of the data (size_t).
+#define PW_TRACE_END_DATA_FLAG(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_TRACE_END_DATA_ARGS, __VA_ARGS__)
+
+#ifdef __cplusplus
+#ifndef PW_TRACE_SCOPE
+// PW_TRACE_SCOPE(label)
+// PW_TRACE_SCOPE(label, group)
+// PW_TRACE_SCOPE(label, group, trace_id)
+//
+// C++ Scope API measures durations until the object loses scope.
+// This can for example, provide a convenient method of tracing
+// functions or loops.
+//
+// Arguments:
+//     label: A string literal which desribes the trace
+//     group <optional>: A string literal which groups this trace with others in
+//                       the same module and group.
+//     trace_id <optional>: A runtime uint32_t which groups this trace with
+//                          others with the same module group and trace_id.
+//                          Every trace with a trace_id must also have a group.
+// Example:
+//   {
+//      PW_TRACE_SCOPE("Bar");
+//      // Do some stuff
+//   }
+//
+//   {
+//      PW_TRACE_SCOPE("Group", "Foo");
+//      {
+//         PW_TRACE_SCOPE("Group", "SubFoo");
+//         // Do some stuff
+//      }
+//      // Do some stuff
+//   }
+//
+//  Which can be visualized as
+//     Bar: [----------------Bar----------------]
+//     Group: [----------------Foo----------------]
+//                        [------SubFoo-------]
+#define PW_TRACE_SCOPE(...) PW_TRACE_SCOPE_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_SCOPE_FLAG(flag, label)
+// PW_TRACE_SCOPE_FLAG(flag, label, group)
+// PW_TRACE_SCOPE_FLAG(flag, label, group, trace_id)
+//
+// These macros mirror PW_TRACE_SCOPE but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+#define PW_TRACE_SCOPE_FLAG(flag, label, ...)                         \
+  _PW_TRACE_SCOPE_OBJECT(                                             \
+      PW_CONCAT(_PwTraceScopeObject, __COUNTER__), flag, __VA_ARGS__) \
+  PW_CONCAT(_pw_trace_scope_object, __COUNTER__)(label);
+#endif  // PW_TRACE_SCOPE
+
+#ifndef PW_TRACE_FUNCTION
+// PW_TRACE_FUNCTION()
+// PW_TRACE_FUNCTION(group)
+// PW_TRACE_FUNCTION(group, trace_id)
+//
+// C++ Function API measures durations until the function returns.
+// This is the same as PW_TRACE_SCOPE, but uses the function name as the label.
+//
+// Arguments:
+//     group <optional>: A string literal which groups this trace with others in
+//                       the same module and group.
+//     trace_id <optional>: A runtime uint32_t which groups this trace with
+//                          others with the same module group and trace_id.
+//                          Every trace with a trace_id must also have a group.
+// Example:
+//   void Bar() {
+//      PW_TRACE_FUNCTION();
+//      // Do some stuff
+//   }
+//
+//   void Child() {
+//      PW_TRACE_FUNCTION("Group");
+//      // Do some stuff
+//   }
+//
+//   void Parent() {
+//      PW_TRACE_FUNCTION("Group");
+//      // Do some stuff
+//      Child();
+//   }
+//
+//  Which can be visualized as
+//     Bar: [----------------Bar----------------]
+//     Group: [----------------Parent----------------]
+//                        [------Child-------]
+#define PW_TRACE_FUNCTION(...) \
+  PW_TRACE_FUNCTION_FLAG(PW_TRACE_FLAGS, __VA_ARGS__)
+
+// PW_TRACE_FUNCTION_FLAG(flag)
+// PW_TRACE_FUNCTION_FLAG(flag, group)
+// PW_TRACE_FUNCTION_FLAG(flag, group, trace_id)
+//
+// These macros mirror PW_TRACE_FUNCTION but intruduce the flag argument to
+// specify a flag value which is used instead of PW_TRACE_FLAGS. The flag goes
+// at the start, group and trace_id arguments are still optional.
+#define PW_TRACE_FUNCTION_FLAG(flag, ...) \
+  PW_TRACE_SCOPE_FLAG(flag, __PRETTY_FUNCTION__, __VA_ARGS__)
+#endif  // PW_TRACE_FUNCTION
+
+#endif  // __cplusplus
diff --git a/pw_trace/pw_trace_test/fake_backend.h b/pw_trace/pw_trace_test/fake_backend.h
new file mode 100644
index 0000000..5e35742
--- /dev/null
+++ b/pw_trace/pw_trace_test/fake_backend.h
@@ -0,0 +1,143 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+
+#pragma once
+
+// Enable traces
+#define PW_TRACE_TYPE_INSTANT trace_fake_backend::Instantaneous
+#define PW_TRACE_TYPE_INSTANT_GROUP trace_fake_backend::InstantaneousGroup
+#define PW_TRACE_TYPE_DURATION_START trace_fake_backend::DurationStart
+#define PW_TRACE_TYPE_DURATION_END trace_fake_backend::DurationEnd
+
+#define PW_TRACE_TYPE_DURATION_GROUP_START \
+  trace_fake_backend::DurationGroupStart
+#define PW_TRACE_TYPE_DURATION_GROUP_END trace_fake_backend::DurationGroupEnd
+
+#define PW_TRACE_TYPE_ASYNC_START trace_fake_backend::AsyncStart
+#define PW_TRACE_TYPE_ASYNC_INSTANT trace_fake_backend::AsyncStep
+#define PW_TRACE_TYPE_ASYNC_END trace_fake_backend::AsyncEnd
+
+namespace trace_fake_backend {
+
+typedef enum {
+  Invalid,
+  Instantaneous,
+  InstantaneousGroup,
+  AsyncStart,
+  AsyncStep,
+  AsyncEnd,
+  DurationStart,
+  DurationEnd,
+  DurationGroupStart,
+  DurationGroupEnd,
+} PwTraceEvent;
+
+// Define a helper class for holding events and checking equality.
+class Event {
+ public:
+  Event()
+      : event_type_(Invalid),
+        flags_(0),
+        label_(nullptr),
+        group_(nullptr),
+        trace_id_(0),
+        has_data_(false),
+        data_format_string_(nullptr),
+        data_(nullptr),
+        data_size_(0) {}
+  Event(PwTraceEvent event_type,
+        uint8_t flags,
+        const char* label,
+        const char* group,
+        uint32_t trace_id)
+      : event_type_(event_type),
+        flags_(flags),
+        label_(label),
+        group_(group),
+        trace_id_(trace_id),
+        has_data_(false),
+        data_format_string_(nullptr),
+        data_(nullptr),
+        data_size_(0) {}
+  Event(PwTraceEvent event_type,
+        uint8_t flags,
+        const char* label,
+        const char* group,
+        uint32_t trace_id,
+        const char* data_type,
+        const char* data,
+        size_t size)
+      : event_type_(event_type),
+        flags_(flags),
+        label_(label),
+        group_(group),
+        trace_id_(trace_id),
+        has_data_(true),
+        data_format_string_(data_type),
+        data_(data),
+        data_size_(size) {}
+  bool operator==(const Event& rhs) const {
+    return event_type_ == rhs.event_type_ &&                      //
+           flags_ == rhs.flags_ &&                                //
+           label_ == rhs.label_ &&                                //
+           group_ == rhs.group_ && trace_id_ == rhs.trace_id_ &&  //
+           has_data_ == rhs.has_data_ &&                          //
+           data_format_string_ == rhs.data_format_string_ &&      //
+           data_size_ == rhs.data_size_ &&                        //
+           (memcmp(data_, rhs.data_, data_size_) == 0);
+  }
+
+  bool IsEqualIgnoreLabel(const Event& rhs) const {
+    return event_type_ == rhs.event_type_ &&                      //
+           flags_ == rhs.flags_ &&                                //
+           group_ == rhs.group_ && trace_id_ == rhs.trace_id_ &&  //
+           has_data_ == rhs.has_data_ &&                          //
+           data_format_string_ == rhs.data_format_string_ &&      //
+           data_size_ == rhs.data_size_ &&                        //
+           (memcmp(data_, rhs.data_, data_size_) == 0);
+  }
+
+ private:
+  PwTraceEvent event_type_;
+  uint8_t flags_;
+  const char* label_;
+  const char* group_;
+  uint32_t trace_id_;
+
+  bool has_data_;
+  const char* data_format_string_;
+  const char* data_;
+  size_t data_size_;
+};
+
+class LastEvent {
+ public:
+  static LastEvent& Instance() { return instance_; }
+  Event& Get() { return last_event_; }
+  void Set(const Event& event) { last_event_ = event; }
+
+ private:
+  Event last_event_{};
+  static LastEvent instance_;
+};
+
+#define PW_TRACE(event_type, flags, label, group, trace_id) \
+  LastEvent::Instance().Set(Event(event_type, flags, label, group, trace_id));
+
+#define PW_TRACE_DATA(                                           \
+    event_type, flags, label, group, trace_id, type, data, size) \
+  LastEvent::Instance().Set(                                     \
+      Event(event_type, flags, label, group, trace_id, type, data, size));
+
+}  // namespace trace_fake_backend
\ No newline at end of file
diff --git a/pw_trace/pw_trace_test/public_overrides/pw_trace_backend/trace_backend.h b/pw_trace/pw_trace_test/public_overrides/pw_trace_backend/trace_backend.h
new file mode 100644
index 0000000..828d80a
--- /dev/null
+++ b/pw_trace/pw_trace_test/public_overrides/pw_trace_backend/trace_backend.h
@@ -0,0 +1,18 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+//==============================================================================
+//
+#pragma once
+
+#include "fake_backend.h"
diff --git a/pw_trace/trace_backend_compile_test.c b/pw_trace/trace_backend_compile_test.c
new file mode 100644
index 0000000..0001775
--- /dev/null
+++ b/pw_trace/trace_backend_compile_test.c
@@ -0,0 +1,62 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+//
+// This is a simple test that ensures a provided trace backend compiles.
+
+#define PW_TRACE_MODULE_NAME "TST"
+
+#include "pw_trace/trace.h"
+
+#ifdef __cplusplus
+#error "This file must be compiled as plain C to verify C compilation works."
+#endif  // __cplusplus
+
+void BasicTraceTestPlainC() {
+  PW_TRACE_INSTANT("Test");
+
+  PW_TRACE_START("Test");
+  PW_TRACE_END("Test");
+
+  PW_TRACE_START("Parent", "group");
+  PW_TRACE_START("Child", "group");
+  PW_TRACE_END("Child", "group");
+  PW_TRACE_INSTANT("Test", "group");
+  PW_TRACE_START("Other Child", "group");
+  PW_TRACE_END("Other Child", "group");
+  PW_TRACE_END("Parent", "group");
+
+  PW_TRACE_START("label for start", "group", 1);
+  PW_TRACE_INSTANT("label for step", "group", 1);
+  PW_TRACE_END("label for end", "group", 1);
+
+  const char kSomeData[] = "SOME DATA";
+  PW_TRACE_INSTANT_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+
+  PW_TRACE_START_DATA("Parent", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_START_DATA("Child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_INSTANT_DATA("Test", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_START_DATA(
+      "Other Child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("Other Child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("Parent", "group", "s", kSomeData, sizeof(kSomeData));
+
+  uint32_t trace_id = 1;
+  PW_TRACE_START_DATA(
+      "label for start", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_INSTANT_DATA(
+      "label for step", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA(
+      "label for end", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+}
diff --git a/pw_trace/trace_backend_compile_test.cc b/pw_trace/trace_backend_compile_test.cc
new file mode 100644
index 0000000..83e7df6
--- /dev/null
+++ b/pw_trace/trace_backend_compile_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+//
+// This is a simple test that ensures a provided trace backend compiles.
+
+#define PW_TRACE_MODULE_NAME "TST"
+#include "gtest/gtest.h"
+#include "pw_trace/trace.h"
+
+namespace {
+
+void TraceFunction() { PW_TRACE_FUNCTION(); }
+void TraceFunctionGroup() { PW_TRACE_FUNCTION("FunctionGroup"); }
+
+const char kSomeData[] = "SOME DATA";
+
+}  // namespace
+
+TEST(BasicTrace, Instant) { PW_TRACE_INSTANT("Test"); }
+
+TEST(BasicTrace, InstantGroup) { PW_TRACE_INSTANT("Test", "group"); }
+
+TEST(BasicTrace, Duration) {
+  PW_TRACE_START("Test");
+  PW_TRACE_END("Test");
+}
+
+TEST(BasicTrace, DurationGroup) {
+  PW_TRACE_START("Parent", "group");
+  PW_TRACE_START("Child", "group");
+  PW_TRACE_END("child", "group");
+  PW_TRACE_START("Other Child", "group");
+  PW_TRACE_END("Other Child", "group");
+  PW_TRACE_END("Parent", "group");
+}
+
+TEST(BasicTrace, Async) {
+  uint32_t trace_id = 1;
+  PW_TRACE_START("label for start", "group", trace_id);
+  PW_TRACE_INSTANT("label for step", "group", trace_id);
+  PW_TRACE_END("label for end", "group", trace_id);
+}
+
+TEST(BasicTrace, Scope) { PW_TRACE_SCOPE("scoped trace"); }
+
+TEST(BasicTrace, ScopeGroup) {
+  PW_TRACE_SCOPE("scoped group trace", "group");
+  { PW_TRACE_SCOPE("sub scoped group trace", "group"); }
+}
+
+TEST(BasicTrace, Function) { TraceFunction(); }
+
+TEST(BasicTrace, FunctionGroup) { TraceFunctionGroup(); }
+
+TEST(BasicTrace, InstantData) {
+  PW_TRACE_INSTANT_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+}
+
+TEST(BasicTrace, InstantGroupData) {
+  PW_TRACE_INSTANT_DATA("Test", "Group", "s", kSomeData, sizeof(kSomeData));
+}
+
+TEST(BasicTrace, DurationData) {
+  PW_TRACE_START_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+}
+
+TEST(BasicTrace, DurationGroupData) {
+  PW_TRACE_START_DATA("Parent", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_START_DATA("Child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_START_DATA(
+      "Other Child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("Other Child", "group", "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA("Parent", "group", "s", kSomeData, sizeof(kSomeData));
+}
+
+TEST(BasicTrace, AsyncData) {
+  uint32_t trace_id = 1;
+  PW_TRACE_START_DATA(
+      "label for start", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_INSTANT_DATA(
+      "label for step", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  PW_TRACE_END_DATA(
+      "label for end", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+}
diff --git a/pw_trace/trace_facade_test.cc b/pw_trace/trace_facade_test.cc
new file mode 100644
index 0000000..a10151e
--- /dev/null
+++ b/pw_trace/trace_facade_test.cc
@@ -0,0 +1,382 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+
+#define PW_TRACE_MODULE_NAME "TST"
+#include "gtest/gtest.h"
+#include "pw_trace/trace.h"
+#include "pw_trace_test/fake_backend.h"
+
+using namespace trace_fake_backend;
+
+// Used by fake backend
+LastEvent LastEvent::instance_;
+
+namespace {
+
+void TraceFunction() {
+  PW_TRACE_FUNCTION();
+  // Can't check label, since might change depending on compiler.
+  EXPECT_TRUE(LastEvent::Instance().Get().IsEqualIgnoreLabel(
+      Event(DurationStart,
+            PW_TRACE_FLAGS_DEFAULT,
+            nullptr,
+            PW_TRACE_GROUP_LABEL_DEFAULT,
+            PW_TRACE_TRACE_ID_DEFAULT)));
+}
+
+void TraceFunctionGroup() {
+  PW_TRACE_FUNCTION("FunctionGroup");
+  // Can't check label, since might change depending on compiler.
+  EXPECT_TRUE(LastEvent::Instance().Get().IsEqualIgnoreLabel(
+      Event(DurationGroupStart,
+            PW_TRACE_FLAGS_DEFAULT,
+            nullptr,
+            "FunctionGroup",
+            PW_TRACE_TRACE_ID_DEFAULT)));
+}
+
+const char kSomeData[] = "SOME DATA";
+
+}  // namespace
+
+TEST(BasicTrace, Instant) {
+  PW_TRACE_INSTANT("Test");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(Instantaneous,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, InstantGroup) {
+  PW_TRACE_INSTANT("Test", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(InstantaneousGroup,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, Duration) {
+  PW_TRACE_START("Test");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+  PW_TRACE_END("Test");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, DurationGroup) {
+  PW_TRACE_START("Parent", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Parent",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+  PW_TRACE_START("Child", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Child",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+  PW_TRACE_END("Child", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Child",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+  PW_TRACE_START("Other Child", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Other Child",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+  PW_TRACE_END("Other Child", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Other Child",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+  PW_TRACE_END("Parent", "group");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Parent",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, Async) {
+  uint32_t trace_id = 1;
+  PW_TRACE_START("async", "group", trace_id);
+  EXPECT_EQ(
+      LastEvent::Instance().Get(),
+      Event(AsyncStart, PW_TRACE_FLAGS_DEFAULT, "async", "group", trace_id));
+  PW_TRACE_INSTANT("step", "group", trace_id);
+  EXPECT_EQ(
+      LastEvent::Instance().Get(),
+      Event(AsyncStep, PW_TRACE_FLAGS_DEFAULT, "step", "group", trace_id));
+  PW_TRACE_END("async", "group", trace_id);
+  EXPECT_EQ(
+      LastEvent::Instance().Get(),
+      Event(AsyncEnd, PW_TRACE_FLAGS_DEFAULT, "async", "group", trace_id));
+}
+
+TEST(BasicTrace, Scope) {
+  {
+    PW_TRACE_SCOPE("scoped trace");
+    EXPECT_EQ(LastEvent::Instance().Get(),
+              Event(DurationStart,
+                    PW_TRACE_FLAGS_DEFAULT,
+                    "scoped trace",
+                    PW_TRACE_GROUP_LABEL_DEFAULT,
+                    PW_TRACE_TRACE_ID_DEFAULT));
+  }
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "scoped trace",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, ScopeGroup) {
+  {
+    PW_TRACE_SCOPE("scoped group trace", "group");
+    EXPECT_EQ(LastEvent::Instance().Get(),
+              Event(DurationGroupStart,
+                    PW_TRACE_FLAGS_DEFAULT,
+                    "scoped group trace",
+                    "group",
+                    PW_TRACE_TRACE_ID_DEFAULT));
+    {
+      PW_TRACE_SCOPE("sub scoped group trace", "group");
+      EXPECT_EQ(LastEvent::Instance().Get(),
+                Event(DurationGroupStart,
+                      PW_TRACE_FLAGS_DEFAULT,
+                      "sub scoped group trace",
+                      "group",
+                      PW_TRACE_TRACE_ID_DEFAULT));
+    }
+    EXPECT_EQ(LastEvent::Instance().Get(),
+              Event(DurationGroupEnd,
+                    PW_TRACE_FLAGS_DEFAULT,
+                    "sub scoped group trace",
+                    "group",
+                    PW_TRACE_TRACE_ID_DEFAULT));
+  }
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "scoped group trace",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, Function) {
+  TraceFunction();
+  // Can't check label, since might change depending on compiler.
+  EXPECT_TRUE(LastEvent::Instance().Get().IsEqualIgnoreLabel(
+      Event(DurationEnd,
+            PW_TRACE_FLAGS_DEFAULT,
+            nullptr,
+            PW_TRACE_GROUP_LABEL_DEFAULT,
+            PW_TRACE_TRACE_ID_DEFAULT)));
+}
+
+TEST(BasicTrace, FunctionGroup) {
+  TraceFunctionGroup();
+  // Can't check label, since might change depending on compiler.
+  EXPECT_TRUE(LastEvent::Instance().Get().IsEqualIgnoreLabel(
+      Event(DurationGroupEnd,
+            PW_TRACE_FLAGS_DEFAULT,
+            nullptr,
+            "FunctionGroup",
+            PW_TRACE_TRACE_ID_DEFAULT)));
+}
+
+TEST(BasicTrace, InstantData) {
+  PW_TRACE_INSTANT_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(Instantaneous,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+}
+
+TEST(BasicTrace, InstantGroupData) {
+  PW_TRACE_INSTANT_DATA("Test", "Group", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(InstantaneousGroup,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  "Group",
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+}
+
+TEST(BasicTrace, DurationData) {
+  PW_TRACE_START_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+  PW_TRACE_END_DATA("Test", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+}
+
+TEST(BasicTrace, DurationGroupData) {
+  PW_TRACE_START_DATA("Parent", "group", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Parent",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+  PW_TRACE_START_DATA("Child", "group", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Child",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+  PW_TRACE_END_DATA("Child", "group", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Child",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+  PW_TRACE_END_DATA("Parent", "group", "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(DurationGroupEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Parent",
+                  "group",
+                  PW_TRACE_TRACE_ID_DEFAULT,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+}
+
+TEST(BasicTrace, AsyncData) {
+  uint32_t trace_id = 1;
+  PW_TRACE_START_DATA(
+      "label for start", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(AsyncStart,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "label for start",
+                  "group",
+                  trace_id,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+  PW_TRACE_INSTANT_DATA(
+      "label for step", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(AsyncStep,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "label for step",
+                  "group",
+                  trace_id,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+  PW_TRACE_END_DATA(
+      "label for end", "group", trace_id, "s", kSomeData, sizeof(kSomeData));
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(AsyncEnd,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "label for end",
+                  "group",
+                  trace_id,
+                  "s",
+                  kSomeData,
+                  sizeof(kSomeData)));
+}
+
+TEST(BasicTrace, ProvideFlag) {
+  PW_TRACE_INSTANT_FLAG(5, "Test");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(Instantaneous,
+                  5,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
+
+TEST(BasicTrace, MacroFlag) {
+#undef PW_TRACE_FLAGS
+#define PW_TRACE_FLAGS 6
+  PW_TRACE_INSTANT("Test");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(Instantaneous,
+                  6,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+#undef PW_TRACE_FLAGS
+#define PW_TRACE_FLAGS PW_TRACE_FLAGS_DEFAULT
+  PW_TRACE_INSTANT("Test");
+  EXPECT_EQ(LastEvent::Instance().Get(),
+            Event(Instantaneous,
+                  PW_TRACE_FLAGS_DEFAULT,
+                  "Test",
+                  PW_TRACE_GROUP_LABEL_DEFAULT,
+                  PW_TRACE_TRACE_ID_DEFAULT));
+}
diff --git a/pw_vars_default.gni b/pw_vars_default.gni
index 10393ee..0c86cab 100644
--- a/pw_vars_default.gni
+++ b/pw_vars_default.gni
@@ -112,6 +112,9 @@
 # Backend for the pw_log module.
 dir_pw_log_backend = ""
 
+# Backend for the pw_trace module.
+dir_pw_trace_backend = ""
+
 # Backend for the pw_sys_io module.
 dir_pw_sys_io_backend = ""