xref: /aosp_15_r20/external/pigweed/pw_trace/example/sample_app.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 //==============================================================================
15 // ninja -C out/host trace_sample
16 // ./out/host/obj/pw_trace_tokenized/trace_sample
17 // python pw_trace_tokenized/py/trace.py -i out1.bin -o trace.json
18 //       ./out/host/obj/pw_trace_tokenized/trace_sample
19 #include <stdio.h>
20 
21 #include <array>
22 #include <chrono>
23 
24 #include "pw_ring_buffer/prefixed_entry_ring_buffer.h"
25 #include "pw_trace/trace.h"
26 
27 #ifndef SAMPLE_APP_SLEEP_MILLIS
28 #include <thread>
29 #define SAMPLE_APP_SLEEP_MILLIS(millis) \
30   std::this_thread::sleep_for(std::chrono::milliseconds(millis));
31 #endif  // SAMPLE_APP_SLEEP_MILLIS
32 
33 using namespace std::chrono;
34 
35 namespace {
36 
37 // Time helper function
38 auto start = system_clock::now();
GetTimeSinceBootMillis()39 uint32_t GetTimeSinceBootMillis() {
40   auto delta = system_clock::now() - start;
41   return floor<milliseconds>(delta).count();
42 }
43 
44 // Creating a very simple runnable with predictable behaviour to help with the
45 // example. Each Runnable, has a method ShouldRun which indicates if it has work
46 // to do, calling Run will then do the work.
47 class SimpleRunnable {
48  public:
49   virtual const char* Name() const = 0;
50   virtual bool ShouldRun() = 0;
51   virtual void Run() = 0;
~SimpleRunnable()52   virtual ~SimpleRunnable() {}
53 };
54 
55 // Processing module
56 // Uses trace_id and groups to track the multiple stages of "processing".
57 // These are intentionally long running so they will be processing concurrently.
58 // The trace ID is used to seperates these concurrent jobs.
59 #undef PW_TRACE_MODULE_NAME
60 #define PW_TRACE_MODULE_NAME "Processing"
61 class ProcessingTask : public SimpleRunnable {
62  public:
63   // Run task maintains a buffer of "jobs" which just sleeps for an amount of
64   // time and reposts the job until the value is zero. This gives an async
65   // behaviour where multiple of the same job are happening concurrently, and
66   // also has a nesting effect of a job having many stages.
67   struct Job {
68     uint32_t job_id;
69     uint8_t value;
70   };
71   struct JobBytes {
72     union {
73       Job job;
74       std::byte bytes[sizeof(Job)];
75     };
76   };
ProcessingTask()77   ProcessingTask() {
78     // Buffer is used for the job queue.
79     pw::span<std::byte> buf_span = pw::span<std::byte>(
80         reinterpret_cast<std::byte*>(jobs_buffer_), sizeof(jobs_buffer_));
81     jobs_.SetBuffer(buf_span)
82         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
83   }
Name() const84   const char* Name() const override { return "Processing Task"; }
ShouldRun()85   bool ShouldRun() override { return jobs_.EntryCount() > 0; }
Run()86   void Run() override {
87     JobBytes job_bytes;
88     size_t bytes_read;
89 
90     // Trace the job count backlog
91     size_t entry_count = jobs_.EntryCount();
92 
93     // Get the next job from the queue.
94     jobs_.PeekFront(job_bytes.bytes, &bytes_read)
95         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
96     jobs_.PopFront()
97         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
98     Job& job = job_bytes.job;
99 
100     // Process the job
101     ProcessingJob(job);
102     if (job.value > 0) {  // repost for more work if value > 0
103       AddJobInternal(job.job_id, job.value - 1);
104     } else {
105       PW_TRACE_END("Job", "Process", job.job_id);
106     }
107     PW_TRACE_INSTANT_DATA("job_backlog_count",
108                           "@pw_arg_counter",
109                           &entry_count,
110                           sizeof(entry_count));
111   }
AddJob(uint32_t job_id,uint8_t value)112   void AddJob(uint32_t job_id, uint8_t value) {
113     PW_TRACE_START_DATA(
114         "Job", "Process", job_id, "@pw_py_struct_fmt:B", &value, sizeof(value));
115     AddJobInternal(job_id, value);
116   }
117 
118  private:
119   static constexpr size_t kMaxJobs = 10;
120   static constexpr size_t kProcessingTimePerValueMillis = 250;
121   Job jobs_buffer_[kMaxJobs];
122   pw::ring_buffer::PrefixedEntryRingBuffer jobs_{false};
123 
ProcessingJob(const Job & job)124   void ProcessingJob(const Job& job) {
125     PW_TRACE_FUNCTION("Process", job.job_id);
126     for (uint8_t i = 0; i < job.value; i++) {
127       PW_TRACE_SCOPE("loop", "Process", job.job_id);
128       SAMPLE_APP_SLEEP_MILLIS(50);  // Fake processing time
129       SomeProcessing(&job);
130     }
131   }
132 
SomeProcessing(const Job * job)133   void SomeProcessing(const Job* job) {
134     uint32_t id = job->job_id;
135     PW_TRACE_FUNCTION("Process", id);
136     SAMPLE_APP_SLEEP_MILLIS(
137         kProcessingTimePerValueMillis);  // Fake processing time
138   }
AddJobInternal(uint32_t job_id,uint8_t value)139   void AddJobInternal(uint32_t job_id, uint8_t value) {
140     JobBytes job{.job = {.job_id = job_id, .value = value}};
141     jobs_.PushBack(job.bytes)
142         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
143   }
144 } processing_task;
145 
146 // Input Module
147 // Uses traces in groups to indicate the different steps of reading the new
148 // event.
149 // Uses an instant data event to dump the read sample into the trace.
150 #undef PW_TRACE_MODULE_NAME
151 #define PW_TRACE_MODULE_NAME "Input"
152 class InputTask : public SimpleRunnable {
153   // Every second generate new output
154  public:
Name() const155   const char* Name() const override { return "Input Task"; }
ShouldRun()156   bool ShouldRun() override {
157     return (GetTimeSinceBootMillis() - last_run_time_ > kRunInterval);
158   }
Run()159   void Run() override {
160     last_run_time_ = GetTimeSinceBootMillis();
161     PW_TRACE_FUNCTION("Input");
162     SAMPLE_APP_SLEEP_MILLIS(50);
163     uint8_t value = GetValue();
164     PW_TRACE_INSTANT_DATA("value", "@pw_arg_counter", &value, sizeof(value));
165     processing_task.AddJob(sample_count_, value);
166     sample_count_++;
167   }
168 
169  private:
GetValue()170   uint8_t GetValue() {
171     PW_TRACE_FUNCTION("Input");
172     SAMPLE_APP_SLEEP_MILLIS(100);  // Fake processing time
173     return sample_count_ % 4 + 1;
174   }
175   size_t sample_count_ = 0;
176   uint32_t last_run_time_ = 0;
177   static constexpr uint32_t kRunInterval = 1000;
178 } input_task;
179 
180 // Simple main loop acting as the "Kernel"
181 // Uses simple named trace durations to indicate which task/job is running
182 #undef PW_TRACE_MODULE_NAME
183 #define PW_TRACE_MODULE_NAME "Kernel"
StartFakeKernel()184 void StartFakeKernel() {
185   std::array<SimpleRunnable*, 2> tasks = {&input_task, &processing_task};
186 
187   bool idle = false;
188   while (true) {
189     bool have_any_run = false;
190     for (auto& task : tasks) {
191       if (task->ShouldRun()) {
192         if (idle) {
193           PW_TRACE_END("Idle", "Idle");
194           idle = false;
195         }
196         have_any_run = true;
197         // The task name is not a string literal and is therefore put in the
198         // data section, so it can also work with tokenized trace.
199         PW_TRACE_START_DATA(
200             "Running", "@pw_arg_group", task->Name(), strlen(task->Name()));
201         task->Run();
202         PW_TRACE_END_DATA(
203             "Running", "@pw_arg_group", task->Name(), strlen(task->Name()));
204       }
205     }
206     if (!idle && !have_any_run) {
207       PW_TRACE_START("Idle", "Idle");
208       idle = true;
209     }
210   }
211 }
212 
213 }  // namespace
214 
RunTraceSampleApp()215 void RunTraceSampleApp() { StartFakeKernel(); }
216