Add a ProtoLog message decoder class

Used to decode ProtoLog message using the interned ViewerConfig.

Test: tools/ninja -C out/linux_clang_release && tools/diff_test_trace_processor.py ./out/linux_clang_release/trace_processor_shell --name-filter "ProtoLog"
Change-Id: I3ba178e46f8686fbe1f851e2941363f377c68bef
diff --git a/Android.bp b/Android.bp
index 7c6d340..63bebff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12526,6 +12526,7 @@
     name: "perfetto_src_trace_processor_importers_proto_winscope_full",
     srcs: [
         "src/trace_processor/importers/proto/winscope/android_input_event_parser.cc",
+        "src/trace_processor/importers/proto/winscope/protolog_message_decoder.cc",
         "src/trace_processor/importers/proto/winscope/protolog_messages_tracker.cc",
         "src/trace_processor/importers/proto/winscope/protolog_parser.cc",
         "src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc",
diff --git a/BUILD b/BUILD
index 52bb806..51b3356 100644
--- a/BUILD
+++ b/BUILD
@@ -1768,6 +1768,8 @@
     srcs = [
         "src/trace_processor/importers/proto/winscope/android_input_event_parser.cc",
         "src/trace_processor/importers/proto/winscope/android_input_event_parser.h",
+        "src/trace_processor/importers/proto/winscope/protolog_message_decoder.cc",
+        "src/trace_processor/importers/proto/winscope/protolog_message_decoder.h",
         "src/trace_processor/importers/proto/winscope/protolog_messages_tracker.cc",
         "src/trace_processor/importers/proto/winscope/protolog_messages_tracker.h",
         "src/trace_processor/importers/proto/winscope/protolog_parser.cc",
diff --git a/src/trace_processor/importers/proto/winscope/BUILD.gn b/src/trace_processor/importers/proto/winscope/BUILD.gn
index 682fc4b..ab09809 100644
--- a/src/trace_processor/importers/proto/winscope/BUILD.gn
+++ b/src/trace_processor/importers/proto/winscope/BUILD.gn
@@ -18,6 +18,8 @@
   sources = [
     "android_input_event_parser.cc",
     "android_input_event_parser.h",
+    "protolog_message_decoder.cc",
+    "protolog_message_decoder.h",
     "protolog_messages_tracker.cc",
     "protolog_messages_tracker.h",
     "protolog_parser.cc",
diff --git a/src/trace_processor/importers/proto/winscope/protolog_message_decoder.cc b/src/trace_processor/importers/proto/winscope/protolog_message_decoder.cc
new file mode 100644
index 0000000..739c540
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/protolog_message_decoder.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 "src/trace_processor/importers/proto/winscope/protolog_message_decoder.h"
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto::trace_processor {
+
+ProtoLogMessageDecoder::ProtoLogMessageDecoder() = default;
+ProtoLogMessageDecoder::~ProtoLogMessageDecoder() = default;
+
+std::optional<DecodedMessage> ProtoLogMessageDecoder::Decode(
+    uint64_t message_id,
+    const std::vector<int64_t>& sint64_params,
+    const std::vector<double>& double_params,
+    const std::vector<bool>& boolean_params,
+    const std::vector<std::string>& string_params) {
+  auto tracked_message = tracked_messages_.Find(message_id);
+  if (tracked_message == nullptr) {
+    return std::nullopt;
+  }
+
+  auto message = tracked_message->message;
+
+  auto group = tracked_groups_.Find(tracked_message->group_id);
+  if (group == nullptr) {
+    return std::nullopt;
+  }
+
+  std::string formatted_message;
+  formatted_message.reserve(message.size());
+
+  auto sint64_params_itr = sint64_params.begin();
+  auto double_params_itr = double_params.begin();
+  auto boolean_params_itr = boolean_params.begin();
+  auto str_params_itr = string_params.begin();
+
+  for (size_t i = 0; i < message.length();) {
+    if (message.at(i) == '%' && i + 1 < message.length()) {
+      switch (message.at(i + 1)) {
+        case '%':
+          break;
+        case 'd': {
+          base::StackString<32> param("%" PRId64, *sint64_params_itr);
+          formatted_message.append(param.c_str());
+          ++sint64_params_itr;
+          break;
+        }
+        case 'o': {
+          base::StackString<32> param("%" PRIo64, *sint64_params_itr);
+          formatted_message.append(param.c_str());
+          ++sint64_params_itr;
+          break;
+        }
+        case 'x': {
+          base::StackString<32> param("%" PRIx64, *sint64_params_itr);
+          formatted_message.append(param.c_str());
+          ++sint64_params_itr;
+          break;
+        }
+        case 'f': {
+          base::StackString<32> param("%f", *double_params_itr);
+          formatted_message.append(param.c_str());
+          ++double_params_itr;
+          break;
+        }
+        case 'e': {
+          base::StackString<32> param("%e", *double_params_itr);
+          formatted_message.append(param.c_str());
+          ++double_params_itr;
+          break;
+        }
+        case 'g': {
+          base::StackString<32> param("%g", *double_params_itr);
+          formatted_message.append(param.c_str());
+          ++double_params_itr;
+          break;
+        }
+        case 's': {
+          formatted_message.append(*str_params_itr);
+          ++str_params_itr;
+          break;
+        }
+        case 'b': {
+          formatted_message.append(*boolean_params_itr ? "true" : "false");
+          ++boolean_params_itr;
+          break;
+        }
+        default:
+          formatted_message.push_back(message[i]);
+          formatted_message.push_back(message[i + 1]);
+      }
+
+      i += 2;
+    } else {
+      formatted_message.push_back(message[i]);
+      i += 1;
+    }
+  }
+
+  return DecodedMessage{tracked_message->level, group->tag, formatted_message};
+}
+
+void ProtoLogMessageDecoder::TrackGroup(uint32_t id, const std::string& tag) {
+  tracked_groups_.Insert(id, TrackedGroup{tag});
+}
+
+void ProtoLogMessageDecoder::TrackMessage(uint64_t message_id,
+                                          ProtoLogLevel level,
+                                          uint32_t group_id,
+                                          const std::string& message) {
+  tracked_messages_.Insert(message_id,
+                           TrackedMessage{level, group_id, message});
+}
+
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/importers/proto/winscope/protolog_message_decoder.h b/src/trace_processor/importers/proto/winscope/protolog_message_decoder.h
new file mode 100644
index 0000000..55388b4
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/protolog_message_decoder.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_PROTOLOG_MESSAGE_DECODER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_PROTOLOG_MESSAGE_DECODER_H_
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/winscope_tables_py.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto::trace_processor {
+
+enum ProtoLogLevel : int32_t {
+  DEBUG = 1,
+  VERBOSE = 2,
+  INFO = 3,
+  WARN = 4,
+  ERROR = 5,
+  WTF = 6,
+};
+
+struct DecodedMessage {
+  ProtoLogLevel log_level;
+  std::string group_tag;
+  std::string message;
+};
+
+struct TrackedGroup {
+  std::string tag;
+};
+
+struct TrackedMessage {
+  ProtoLogLevel level;
+  uint32_t group_id;
+  std::string message;
+};
+
+class ProtoLogMessageDecoder : public Destructible {
+ public:
+  explicit ProtoLogMessageDecoder();
+  virtual ~ProtoLogMessageDecoder() override;
+
+  static ProtoLogMessageDecoder* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->protolog_message_decoder) {
+      context->protolog_message_decoder.reset(new ProtoLogMessageDecoder());
+    }
+    return static_cast<ProtoLogMessageDecoder*>(
+        context->protolog_message_decoder.get());
+  }
+
+  std::optional<DecodedMessage> Decode(
+      uint64_t message_id,
+      const std::vector<int64_t>& sint64_params,
+      const std::vector<double>& double_params,
+      const std::vector<bool>& boolean_params,
+      const std::vector<std::string>& string_params);
+
+  void TrackGroup(uint32_t id, const std::string& tag);
+
+  void TrackMessage(uint64_t message_id,
+                    ProtoLogLevel level,
+                    uint32_t group_id,
+                    const std::string& message);
+
+ private:
+  base::FlatHashMap<uint64_t, TrackedGroup> tracked_groups_;
+  base::FlatHashMap<uint64_t, TrackedMessage> tracked_messages_;
+};
+
+}  // namespace perfetto::trace_processor
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_PROTOLOG_MESSAGE_DECODER_H_
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 3ec964f..63c3ee7 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -146,6 +146,7 @@
   std::unique_ptr<Destructible> v8_tracker;                // V8Tracker
   std::unique_ptr<Destructible> jit_tracker;               // JitTracker
   std::unique_ptr<Destructible> perf_dso_tracker;          // DsoTracker
+  std::unique_ptr<Destructible> protolog_message_decoder;  // ProtoLogMessageDecoder
   // clang-format on
 
   std::unique_ptr<ProtoTraceParser> proto_trace_parser;