• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifdef __ANDROID__
18 #include "hal/snoop_logger_tracing.h"
19 
20 #include <bluetooth/log.h>
21 #include <perfetto/trace/android/bluetooth_trace.pbzero.h>
22 #include <perfetto/tracing.h>
23 
24 #include "hal/snoop_logger.h"
25 #include "hci/hci_packets.h"
26 
27 PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(bluetooth::hal::SnoopLoggerTracing);
28 
29 using perfetto::protos::pbzero::BluetoothTracePacketType;
30 
31 namespace bluetooth {
32 namespace hal {
33 namespace {
34 
35 // The Perfetto trace flush interval in nanoseconds.
36 constexpr uint64_t TRACE_FLUSH_INTERVAL_NANOS = 100 * 1000 * 1000;
37 
SkipTracePoint(const HciPacket & packet,SnoopLogger::PacketType type)38 static bool SkipTracePoint(const HciPacket& packet, SnoopLogger::PacketType type) {
39   if (type == SnoopLogger::PacketType::EVT) {
40     uint8_t evt_code = packet[0];
41 
42     // Below set of commands does not provide further insight into bluetooth
43     // behavior. Skip these to save bluetooth tracing from becoming too large.
44     return evt_code == static_cast<uint8_t>(hci::EventCode::NUMBER_OF_COMPLETED_PACKETS) ||
45            evt_code == static_cast<uint8_t>(hci::EventCode::COMMAND_COMPLETE) ||
46            evt_code == static_cast<uint8_t>(hci::EventCode::COMMAND_STATUS);
47   }
48 
49   return false;
50 }
51 }  // namespace
52 
BundleKey(const HciPacket & packet,SnoopLogger::Direction direction,SnoopLogger::PacketType type)53 BundleKey::BundleKey(const HciPacket& packet, SnoopLogger::Direction direction,
54                      SnoopLogger::PacketType type)
55     : packet_type(type), direction(direction) {
56   switch (type) {
57     case SnoopLogger::PacketType::EVT: {
58       event_code = packet[0];
59 
60       if (event_code == static_cast<uint8_t>(hci::EventCode::LE_META_EVENT) ||
61           event_code == static_cast<uint8_t>(hci::EventCode::VENDOR_SPECIFIC)) {
62         subevent_code = packet[2];
63       }
64     } break;
65     case SnoopLogger::PacketType::CMD: {
66       op_code = packet[0] | (packet[1] << 8);
67     } break;
68     case SnoopLogger::PacketType::ACL:
69     case SnoopLogger::PacketType::ISO:
70     case SnoopLogger::PacketType::SCO: {
71       handle = (packet[0] | (packet[1] << 8)) & 0x0fff;
72     } break;
73   }
74 }
75 
76 #define AGG_FIELDS(x) \
77   (x).packet_type, (x).direction, (x).event_code, (x).subevent_code, (x).op_code, (x).handle
78 
operator ==(const BundleKey & b) const79 bool BundleKey::operator==(const BundleKey& b) const {
80   return std::tie(AGG_FIELDS(*this)) == std::tie(AGG_FIELDS(b));
81 }
82 
83 template <typename T, typename... Rest>
HashCombine(std::size_t & seed,const T & val,const Rest &...rest)84 void HashCombine(std::size_t& seed, const T& val, const Rest&... rest) {
85   seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
86   (HashCombine(seed, rest), ...);
87 }
88 
operator ()(const BundleKey & a) const89 std::size_t BundleHash::operator()(const BundleKey& a) const {
90   std::size_t seed = 0;
91   HashCombine(seed, AGG_FIELDS(a));
92   return seed;
93 }
94 
95 #undef AGG_FIELDS
96 
InitializePerfetto()97 void SnoopLoggerTracing::InitializePerfetto() {
98   perfetto::TracingInitArgs args;
99   args.backends |= perfetto::kSystemBackend;
100 
101   perfetto::Tracing::Initialize(args);
102   perfetto::DataSourceDescriptor dsd;
103   dsd.set_name("android.bluetooth_tracing");
104   SnoopLoggerTracing::Register(dsd);
105 }
106 
HciToTracePacketType(SnoopLogger::PacketType hci_packet_type,SnoopLogger::Direction direction)107 BluetoothTracePacketType SnoopLoggerTracing::HciToTracePacketType(
108         SnoopLogger::PacketType hci_packet_type, SnoopLogger::Direction direction) {
109   BluetoothTracePacketType trace_packet_type;
110   switch (hci_packet_type) {
111     case SnoopLogger::PacketType::CMD: {
112       trace_packet_type = BluetoothTracePacketType::HCI_CMD;
113     } break;
114     case SnoopLogger::PacketType::EVT: {
115       trace_packet_type = BluetoothTracePacketType::HCI_EVT;
116     } break;
117     case SnoopLogger::PacketType::ACL: {
118       if (direction == SnoopLogger::INCOMING) {
119         trace_packet_type = BluetoothTracePacketType::HCI_ACL_RX;
120       } else {
121         trace_packet_type = BluetoothTracePacketType::HCI_ACL_TX;
122       }
123     } break;
124     case SnoopLogger::PacketType::ISO: {
125       if (direction == SnoopLogger::INCOMING) {
126         trace_packet_type = BluetoothTracePacketType::HCI_ISO_RX;
127       } else {
128         trace_packet_type = BluetoothTracePacketType::HCI_ISO_TX;
129       }
130     } break;
131     case SnoopLogger::PacketType::SCO: {
132       if (direction == SnoopLogger::INCOMING) {
133         trace_packet_type = BluetoothTracePacketType::HCI_SCO_RX;
134       } else {
135         trace_packet_type = BluetoothTracePacketType::HCI_SCO_TX;
136       }
137     } break;
138   }
139   return trace_packet_type;
140 }
141 
TracePacket(const HciPacket & packet,SnoopLogger::Direction direction,SnoopLogger::PacketType type)142 void SnoopLoggerTracing::TracePacket(const HciPacket& packet, SnoopLogger::Direction direction,
143                                      SnoopLogger::PacketType type) {
144   if (SkipTracePoint(packet, type)) {
145     return;
146   }
147 
148   SnoopLoggerTracing::Trace([&](SnoopLoggerTracing::TraceContext ctx) {
149     perfetto::LockedHandle<SnoopLoggerTracing> handle = ctx.GetDataSourceLocked();
150     if (handle.valid()) {
151       handle->Record(ctx, packet, direction, type);
152     }
153   });
154 }
155 
Record(TraceContext & ctx,const HciPacket & packet,SnoopLogger::Direction direction,SnoopLogger::PacketType type)156 void SnoopLoggerTracing::Record(TraceContext& ctx, const HciPacket& packet,
157                                 SnoopLogger::Direction direction, SnoopLogger::PacketType type) {
158   // Write pending events before saving the new one to the bundle. Not doing this
159   // includes the new event after a potentially long gap, leading to a bundle with
160   // a very long duration.
161   uint64_t timestamp_ns = perfetto::base::GetBootTimeNs().count();
162   if (last_flush_ns_ + TRACE_FLUSH_INTERVAL_NANOS < timestamp_ns) {
163     for (const auto& [key, details] : bttrace_bundles_) {
164       Write(ctx, key, details);
165     }
166 
167     bttrace_bundles_.clear();
168     last_flush_ns_ = timestamp_ns;
169   }
170 
171   BundleKey key(packet, direction, type);
172 
173   BundleDetails& bundle = bttrace_bundles_[key];
174   bundle.count++;
175   bundle.total_length += packet.size();
176   bundle.start_ts = std::min(bundle.start_ts, timestamp_ns);
177   bundle.end_ts = std::max(bundle.end_ts, timestamp_ns);
178 }
179 
Write(TraceContext & ctx,const BundleKey & key,const BundleDetails & details)180 void SnoopLoggerTracing::Write(TraceContext& ctx, const BundleKey& key,
181                                const BundleDetails& details) {
182   auto trace_pkt = ctx.NewTracePacket();
183   trace_pkt->set_timestamp(details.start_ts);
184   auto* bt_event = trace_pkt->set_bluetooth_trace_event();
185   bt_event->set_packet_type(HciToTracePacketType(key.packet_type, key.direction));
186   bt_event->set_count(details.count);
187   bt_event->set_length(details.total_length);
188   bt_event->set_duration(details.end_ts - details.start_ts);
189   if (key.op_code.has_value()) {
190     bt_event->set_op_code(*key.op_code);
191   }
192   if (key.event_code.has_value()) {
193     bt_event->set_event_code(*key.event_code);
194   }
195   if (key.subevent_code.has_value()) {
196     bt_event->set_subevent_code(*key.subevent_code);
197   }
198   if (key.handle.has_value()) {
199     bt_event->set_connection_handle(*key.handle);
200   }
201 }
202 
OnSetup(const SetupArgs &)203 void SnoopLoggerTracing::OnSetup(const SetupArgs&) {}
OnStart(const StartArgs &)204 void SnoopLoggerTracing::OnStart(const StartArgs&) {}
OnStop(const StopArgs &)205 void SnoopLoggerTracing::OnStop(const StopArgs&) {}
OnFlush(const FlushArgs &)206 void SnoopLoggerTracing::OnFlush(const FlushArgs&) {}
207 
208 }  // namespace hal
209 }  // namespace bluetooth
210 #endif  // __ANDROID__
211