xref: /aosp_15_r20/external/pigweed/pw_thread/thread_snapshot_service.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2022 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/thread_snapshot_service.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include "pw_containers/vector.h"
18*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/decoder.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/raw/server_reader_writer.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_span/span.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/thread_info.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/thread_iteration.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_private/thread_snapshot_service.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_protos/thread.pwpb.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_protos/thread_snapshot_service.pwpb.h"
29*61c4878aSAndroid Build Coastguard Worker 
30*61c4878aSAndroid Build Coastguard Worker namespace pw::thread::proto {
31*61c4878aSAndroid Build Coastguard Worker 
ProtoEncodeThreadInfo(pwpb::SnapshotThreadInfo::StreamEncoder & encoder,const ThreadInfo & thread_info)32*61c4878aSAndroid Build Coastguard Worker Status ProtoEncodeThreadInfo(pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
33*61c4878aSAndroid Build Coastguard Worker                              const ThreadInfo& thread_info) {
34*61c4878aSAndroid Build Coastguard Worker   // Grab the next available Thread slot to write to in the response.
35*61c4878aSAndroid Build Coastguard Worker   pwpb::Thread::StreamEncoder proto_encoder = encoder.GetThreadsEncoder();
36*61c4878aSAndroid Build Coastguard Worker   if (thread_info.thread_name().has_value()) {
37*61c4878aSAndroid Build Coastguard Worker     PW_TRY(proto_encoder.WriteName(thread_info.thread_name().value()));
38*61c4878aSAndroid Build Coastguard Worker   } else {
39*61c4878aSAndroid Build Coastguard Worker     // Name is necessary to identify thread.
40*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
41*61c4878aSAndroid Build Coastguard Worker   }
42*61c4878aSAndroid Build Coastguard Worker   if (thread_info.stack_low_addr().has_value()) {
43*61c4878aSAndroid Build Coastguard Worker     PW_TRY(proto_encoder.WriteStackEndPointer(
44*61c4878aSAndroid Build Coastguard Worker         thread_info.stack_low_addr().value()));
45*61c4878aSAndroid Build Coastguard Worker   }
46*61c4878aSAndroid Build Coastguard Worker   if (thread_info.stack_high_addr().has_value()) {
47*61c4878aSAndroid Build Coastguard Worker     PW_TRY(proto_encoder.WriteStackStartPointer(
48*61c4878aSAndroid Build Coastguard Worker         thread_info.stack_high_addr().value()));
49*61c4878aSAndroid Build Coastguard Worker   } else {
50*61c4878aSAndroid Build Coastguard Worker     // Need stack start pointer to contextualize estimated peak.
51*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
52*61c4878aSAndroid Build Coastguard Worker   }
53*61c4878aSAndroid Build Coastguard Worker   if (thread_info.stack_pointer().has_value()) {
54*61c4878aSAndroid Build Coastguard Worker     PW_TRY(
55*61c4878aSAndroid Build Coastguard Worker         proto_encoder.WriteStackPointer(thread_info.stack_pointer().value()));
56*61c4878aSAndroid Build Coastguard Worker   }
57*61c4878aSAndroid Build Coastguard Worker 
58*61c4878aSAndroid Build Coastguard Worker   if (thread_info.stack_peak_addr().has_value()) {
59*61c4878aSAndroid Build Coastguard Worker     PW_TRY(proto_encoder.WriteStackPointerEstPeak(
60*61c4878aSAndroid Build Coastguard Worker         thread_info.stack_peak_addr().value()));
61*61c4878aSAndroid Build Coastguard Worker   } else {
62*61c4878aSAndroid Build Coastguard Worker     // Peak stack usage reporting is not supported.
63*61c4878aSAndroid Build Coastguard Worker     return Status::Unimplemented();
64*61c4878aSAndroid Build Coastguard Worker   }
65*61c4878aSAndroid Build Coastguard Worker 
66*61c4878aSAndroid Build Coastguard Worker   return proto_encoder.status();
67*61c4878aSAndroid Build Coastguard Worker }
68*61c4878aSAndroid Build Coastguard Worker 
ErrorLog(Status status)69*61c4878aSAndroid Build Coastguard Worker void ErrorLog(Status status) {
70*61c4878aSAndroid Build Coastguard Worker   if (status == Status::Unimplemented()) {
71*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
72*61c4878aSAndroid Build Coastguard Worker         "Peak stack usage reporting not supported by your current OS or "
73*61c4878aSAndroid Build Coastguard Worker         "configuration.");
74*61c4878aSAndroid Build Coastguard Worker   } else if (status == Status::FailedPrecondition()) {
75*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Thread missing information needed by service.");
76*61c4878aSAndroid Build Coastguard Worker   } else if (status == Status::ResourceExhausted()) {
77*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Buffer capacity limit exceeded.");
78*61c4878aSAndroid Build Coastguard Worker   } else if (status != OkStatus()) {
79*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
80*61c4878aSAndroid Build Coastguard Worker         "Failure with error code %d, RPC service was unable to capture thread "
81*61c4878aSAndroid Build Coastguard Worker         "information",
82*61c4878aSAndroid Build Coastguard Worker         status.code());
83*61c4878aSAndroid Build Coastguard Worker   }
84*61c4878aSAndroid Build Coastguard Worker }
85*61c4878aSAndroid Build Coastguard Worker 
DecodeThreadName(ConstByteSpan serialized_path,ConstByteSpan & thread_name)86*61c4878aSAndroid Build Coastguard Worker Status DecodeThreadName(ConstByteSpan serialized_path,
87*61c4878aSAndroid Build Coastguard Worker                         ConstByteSpan& thread_name) {
88*61c4878aSAndroid Build Coastguard Worker   protobuf::Decoder decoder(serialized_path);
89*61c4878aSAndroid Build Coastguard Worker   Status status;
90*61c4878aSAndroid Build Coastguard Worker   while (decoder.Next().ok()) {
91*61c4878aSAndroid Build Coastguard Worker     switch (decoder.FieldNumber()) {
92*61c4878aSAndroid Build Coastguard Worker       case static_cast<uint32_t>(pwpb::Thread::Fields::kName): {
93*61c4878aSAndroid Build Coastguard Worker         status.Update(decoder.ReadBytes(&thread_name));
94*61c4878aSAndroid Build Coastguard Worker       }
95*61c4878aSAndroid Build Coastguard Worker     }
96*61c4878aSAndroid Build Coastguard Worker   }
97*61c4878aSAndroid Build Coastguard Worker   return status;
98*61c4878aSAndroid Build Coastguard Worker }
99*61c4878aSAndroid Build Coastguard Worker 
GetPeakStackUsage(ConstByteSpan request,rpc::RawServerWriter & response_writer)100*61c4878aSAndroid Build Coastguard Worker void ThreadSnapshotService::GetPeakStackUsage(
101*61c4878aSAndroid Build Coastguard Worker     ConstByteSpan request, rpc::RawServerWriter& response_writer) {
102*61c4878aSAndroid Build Coastguard Worker   // For now, ignore the request and just stream all the thread information
103*61c4878aSAndroid Build Coastguard Worker   // back.
104*61c4878aSAndroid Build Coastguard Worker   struct IterationInfo {
105*61c4878aSAndroid Build Coastguard Worker     pwpb::SnapshotThreadInfo::MemoryEncoder encoder;
106*61c4878aSAndroid Build Coastguard Worker     Status status;
107*61c4878aSAndroid Build Coastguard Worker     ConstByteSpan name;
108*61c4878aSAndroid Build Coastguard Worker 
109*61c4878aSAndroid Build Coastguard Worker     // For sending out data by chunks.
110*61c4878aSAndroid Build Coastguard Worker     Vector<size_t>& thread_proto_indices;
111*61c4878aSAndroid Build Coastguard Worker   };
112*61c4878aSAndroid Build Coastguard Worker 
113*61c4878aSAndroid Build Coastguard Worker   ConstByteSpan name_request;
114*61c4878aSAndroid Build Coastguard Worker   if (!request.empty()) {
115*61c4878aSAndroid Build Coastguard Worker     if (const auto status = DecodeThreadName(request, name_request);
116*61c4878aSAndroid Build Coastguard Worker         !status.ok()) {
117*61c4878aSAndroid Build Coastguard Worker       PW_LOG_ERROR("Service unable to decode thread name with error code %d",
118*61c4878aSAndroid Build Coastguard Worker                    status.code());
119*61c4878aSAndroid Build Coastguard Worker     }
120*61c4878aSAndroid Build Coastguard Worker   }
121*61c4878aSAndroid Build Coastguard Worker 
122*61c4878aSAndroid Build Coastguard Worker   IterationInfo iteration_info{
123*61c4878aSAndroid Build Coastguard Worker       pwpb::SnapshotThreadInfo::MemoryEncoder(encode_buffer_),
124*61c4878aSAndroid Build Coastguard Worker       OkStatus(),
125*61c4878aSAndroid Build Coastguard Worker       name_request,
126*61c4878aSAndroid Build Coastguard Worker       thread_proto_indices_};
127*61c4878aSAndroid Build Coastguard Worker 
128*61c4878aSAndroid Build Coastguard Worker   iteration_info.thread_proto_indices.clear();
129*61c4878aSAndroid Build Coastguard Worker   iteration_info.thread_proto_indices.push_back(iteration_info.encoder.size());
130*61c4878aSAndroid Build Coastguard Worker 
131*61c4878aSAndroid Build Coastguard Worker   auto cb = [&iteration_info](const ThreadInfo& thread_info) {
132*61c4878aSAndroid Build Coastguard Worker     if (!iteration_info.name.empty() && thread_info.thread_name().has_value()) {
133*61c4878aSAndroid Build Coastguard Worker       if (std::equal(thread_info.thread_name().value().begin(),
134*61c4878aSAndroid Build Coastguard Worker                      thread_info.thread_name().value().end(),
135*61c4878aSAndroid Build Coastguard Worker                      iteration_info.name.begin())) {
136*61c4878aSAndroid Build Coastguard Worker         iteration_info.status.Update(
137*61c4878aSAndroid Build Coastguard Worker             ProtoEncodeThreadInfo(iteration_info.encoder, thread_info));
138*61c4878aSAndroid Build Coastguard Worker         iteration_info.thread_proto_indices.push_back(
139*61c4878aSAndroid Build Coastguard Worker             iteration_info.encoder.size());
140*61c4878aSAndroid Build Coastguard Worker         return false;
141*61c4878aSAndroid Build Coastguard Worker       }
142*61c4878aSAndroid Build Coastguard Worker     } else {
143*61c4878aSAndroid Build Coastguard Worker       iteration_info.status.Update(
144*61c4878aSAndroid Build Coastguard Worker           ProtoEncodeThreadInfo(iteration_info.encoder, thread_info));
145*61c4878aSAndroid Build Coastguard Worker       iteration_info.thread_proto_indices.push_back(
146*61c4878aSAndroid Build Coastguard Worker           iteration_info.encoder.size());
147*61c4878aSAndroid Build Coastguard Worker     }
148*61c4878aSAndroid Build Coastguard Worker     return iteration_info.status.ok();
149*61c4878aSAndroid Build Coastguard Worker   };
150*61c4878aSAndroid Build Coastguard Worker   if (const auto status = ForEachThread(cb); !status.ok()) {
151*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to capture thread information, error %d",
152*61c4878aSAndroid Build Coastguard Worker                  status.code());
153*61c4878aSAndroid Build Coastguard Worker   }
154*61c4878aSAndroid Build Coastguard Worker 
155*61c4878aSAndroid Build Coastguard Worker   // This logging action is external to thread iteration because it is
156*61c4878aSAndroid Build Coastguard Worker   // unsafe to log within ForEachThread() when the scheduler is disabled.
157*61c4878aSAndroid Build Coastguard Worker   ErrorLog(iteration_info.status);
158*61c4878aSAndroid Build Coastguard Worker 
159*61c4878aSAndroid Build Coastguard Worker   Status status;
160*61c4878aSAndroid Build Coastguard Worker   if (iteration_info.encoder.size() && iteration_info.status.ok()) {
161*61c4878aSAndroid Build Coastguard Worker     // Must subtract 1 because the last boundary index of thread_proto_indices
162*61c4878aSAndroid Build Coastguard Worker     // is the end of the last submessage, and NOT the start of another.
163*61c4878aSAndroid Build Coastguard Worker     size_t last_start_index = iteration_info.thread_proto_indices.size() - 1;
164*61c4878aSAndroid Build Coastguard Worker     for (size_t i = 0; i < last_start_index; i += num_bundled_threads_) {
165*61c4878aSAndroid Build Coastguard Worker       const size_t num_threads =
166*61c4878aSAndroid Build Coastguard Worker           std::min(num_bundled_threads_, last_start_index - i);
167*61c4878aSAndroid Build Coastguard Worker 
168*61c4878aSAndroid Build Coastguard Worker       // Sending out a bundle of threads at a time.
169*61c4878aSAndroid Build Coastguard Worker       const size_t bundle_size =
170*61c4878aSAndroid Build Coastguard Worker           iteration_info.thread_proto_indices[i + num_threads] -
171*61c4878aSAndroid Build Coastguard Worker           iteration_info.thread_proto_indices[i];
172*61c4878aSAndroid Build Coastguard Worker 
173*61c4878aSAndroid Build Coastguard Worker       ConstByteSpan thread =
174*61c4878aSAndroid Build Coastguard Worker           ConstByteSpan(iteration_info.encoder.data() +
175*61c4878aSAndroid Build Coastguard Worker                             iteration_info.thread_proto_indices[i],
176*61c4878aSAndroid Build Coastguard Worker                         bundle_size);
177*61c4878aSAndroid Build Coastguard Worker 
178*61c4878aSAndroid Build Coastguard Worker       if (bundle_size) {
179*61c4878aSAndroid Build Coastguard Worker         status.Update(response_writer.Write(thread));
180*61c4878aSAndroid Build Coastguard Worker       }
181*61c4878aSAndroid Build Coastguard Worker       if (!status.ok()) {
182*61c4878aSAndroid Build Coastguard Worker         PW_LOG_ERROR(
183*61c4878aSAndroid Build Coastguard Worker             "Failed to send response with error code %d, packet may be too "
184*61c4878aSAndroid Build Coastguard Worker             "large to send",
185*61c4878aSAndroid Build Coastguard Worker             status.code());
186*61c4878aSAndroid Build Coastguard Worker       }
187*61c4878aSAndroid Build Coastguard Worker     }
188*61c4878aSAndroid Build Coastguard Worker   }
189*61c4878aSAndroid Build Coastguard Worker 
190*61c4878aSAndroid Build Coastguard Worker   if (response_writer.Finish(status) != OkStatus()) {
191*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
192*61c4878aSAndroid Build Coastguard Worker         "Failed to close stream for GetPeakStackUsage() with error code %d",
193*61c4878aSAndroid Build Coastguard Worker         status.code());
194*61c4878aSAndroid Build Coastguard Worker   }
195*61c4878aSAndroid Build Coastguard Worker }
196*61c4878aSAndroid Build Coastguard Worker 
197*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::thread::proto
198