pw_snapshot: Integrate thread state processor

Integrates pw_thread's thread state processor into the snapshot
processor utility.

Change-Id: I0b025b46300ed62a78f0e096cb88283fb005ad36
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/55002
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Armando Montanez <amontanez@google.com>
diff --git a/pw_snapshot/module_usage.rst b/pw_snapshot/module_usage.rst
index a3b2c42..52fb42d 100644
--- a/pw_snapshot/module_usage.rst
+++ b/pw_snapshot/module_usage.rst
@@ -128,3 +128,16 @@
   FW build UUID:     ad2d39258c1bc487f07ca7e04991a836fdf7d0a0
   Snapshot UUID:     8481bb12a162164f5c74855f6d94ea1a
 
+  Thread State
+    2 threads running, Main/Handler active at the time of capture.
+                       ~~~~~~~~~~~~
+
+  Thread (INTERRUPT_HANDLER): Main Stack (Handler Mode) <-- [ACTIVE]
+  Stack info
+    Stack used:   0x2001b000 - 0x2001ae20 (480 bytes)
+    Stack limits: 0x2001b000 - 0x???????? (size unknown)
+
+  Thread (RUNNING): Idle
+  Stack info
+    Stack used:   0x2001ac00 - 0x2001ab0c (244 bytes, 47.66%)
+    Stack limits: 0x2001ac00 - 0x2001aa00 (512 bytes)
diff --git a/pw_snapshot/py/BUILD.gn b/pw_snapshot/py/BUILD.gn
index 8bd0d81..46d8592 100644
--- a/pw_snapshot/py/BUILD.gn
+++ b/pw_snapshot/py/BUILD.gn
@@ -47,6 +47,8 @@
   tests = [ "metadata_test.py" ]
   python_deps = [
     ":pw_snapshot_metadata",
+    "$dir_pw_thread:protos.python",
+    "$dir_pw_thread/py",
     "$dir_pw_tokenizer/py",
     "..:snapshot_proto.python",
   ]
diff --git a/pw_snapshot/py/generate_example_snapshot.py b/pw_snapshot/py/generate_example_snapshot.py
index d9fad17..9554aa0 100644
--- a/pw_snapshot/py/generate_example_snapshot.py
+++ b/pw_snapshot/py/generate_example_snapshot.py
@@ -19,6 +19,29 @@
 from typing import TextIO
 from pw_snapshot_protos import snapshot_pb2
 from pw_snapshot import processor
+from pw_thread_protos import thread_pb2
+
+
+def _add_threads(snapshot: snapshot_pb2.Snapshot) -> snapshot_pb2.Snapshot:
+    # Build example idle thread.
+    thread = thread_pb2.Thread()
+    thread.name = 'Idle'.encode()
+    thread.stack_start_pointer = 0x2001ac00
+    thread.stack_end_pointer = 0x2001aa00
+    thread.stack_pointer = 0x2001ab0c
+    thread.state = thread_pb2.ThreadState.Enum.RUNNING
+    snapshot.threads.append(thread)
+
+    # Build example interrupt handler thread.
+    thread = thread_pb2.Thread()
+    thread.name = 'Main Stack (Handler Mode)'.encode()
+    thread.active = True
+    thread.stack_start_pointer = 0x2001b000
+    thread.stack_pointer = 0x2001ae20
+    thread.state = thread_pb2.ThreadState.Enum.INTERRUPT_HANDLER
+    snapshot.threads.append(thread)
+
+    return snapshot
 
 
 def _main(out_file: TextIO):
@@ -37,6 +60,9 @@
     snapshot.metadata.snapshot_uuid = (b'\x84\x81\xBB\x12\xA1\x62\x16\x4F'
                                        b'\x5C\x74\x85\x5F\x6D\x94\xEA\x1A')
 
+    # Add some thread-related info.
+    snapshot = _add_threads(snapshot)
+
     serialized_snapshot = snapshot.SerializeToString()
     out_file.write(processor.process_snapshots(serialized_snapshot))
 
diff --git a/pw_snapshot/py/pw_snapshot/processor.py b/pw_snapshot/py/pw_snapshot/processor.py
index 3f3357b..3f9cadf 100644
--- a/pw_snapshot/py/pw_snapshot/processor.py
+++ b/pw_snapshot/py/pw_snapshot/processor.py
@@ -19,6 +19,7 @@
 import pw_tokenizer
 from pw_snapshot_metadata import metadata
 from pw_snapshot_protos import snapshot_pb2
+from pw_thread import thread_analyzer
 
 _BRANDING = """
         ____ _       __    _____ _   _____    ____  _____ __  ______  ______
@@ -43,6 +44,11 @@
     if captured_metadata:
         output.append(captured_metadata)
 
+    thread_info = thread_analyzer.process_snapshot(serialized_snapshot,
+                                                   detokenizer)
+    if thread_info:
+        output.append(thread_info)
+
     # Check and emit the number of related snapshots embedded in this snapshot.
     snapshot = snapshot_pb2.Snapshot()
     snapshot.ParseFromString(serialized_snapshot)