Coverage Report

Created: 2026-01-22 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/examples/common/tracing/TraceHandlers.cpp
Line
Count
Source
1
/*
2
 *   Copyright (c) 2021 Project CHIP Authors
3
 *   All rights reserved.
4
 *
5
 *   Licensed under the Apache License, Version 2.0 (the "License");
6
 *   you may not use this file except in compliance with the License.
7
 *   You may obtain a copy of the License at
8
 *
9
 *       http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *   Unless required by applicable law or agreed to in writing, software
12
 *   distributed under the License is distributed on an "AS IS" BASIS,
13
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *   See the License for the specific language governing permissions and
15
 *   limitations under the License.
16
 *
17
 */
18
#include "TraceHandlers.h"
19
20
#include <mutex>
21
#include <stdint.h>
22
#include <string>
23
#include <vector>
24
25
#include <lib/support/BytesToHex.h>
26
#include <lib/support/CodeUtils.h>
27
#include <lib/support/logging/CHIPLogging.h>
28
#include <transport/TraceMessage.h>
29
30
// For `s` std::string literal suffix
31
using namespace std::string_literals;
32
33
namespace chip {
34
namespace trace {
35
36
#if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
37
namespace {
38
39
// Handles the output from the trace handlers.
40
class TraceOutput
41
{
42
public:
43
0
    ~TraceOutput() { UnregisterAllStreams(); }
44
45
    void RegisterStream(TraceStream * stream)
46
0
    {
47
0
        std::lock_guard<std::mutex> guard(mLock);
48
0
        mStreams.push_back(stream);
49
0
    }
50
51
    void UnregisterAllStreams()
52
0
    {
53
0
        for (auto stream : mStreams)
54
0
        {
55
0
            delete stream;
56
0
        }
57
0
        mStreams.clear();
58
0
    }
59
60
    void StartEvent(const std::string & label)
61
0
    {
62
0
        std::lock_guard<std::mutex> guard(mLock);
63
0
        for (auto stream : mStreams)
64
0
        {
65
0
            stream->StartEvent(label);
66
0
        }
67
0
    }
68
69
    void AddField(const std::string & tag, const std::string & data)
70
0
    {
71
0
        std::lock_guard<std::mutex> guard(mLock);
72
0
        for (auto stream : mStreams)
73
0
        {
74
0
            stream->AddField(tag, data);
75
0
        }
76
0
    }
77
78
    void FinishEvent()
79
0
    {
80
0
        std::lock_guard<std::mutex> guard(mLock);
81
0
        for (auto stream : mStreams)
82
0
        {
83
0
            stream->FinishEvent();
84
0
        }
85
0
    }
86
87
    bool HasStreamAvailable() const
88
0
    {
89
0
        std::lock_guard<std::mutex> guard(mLock);
90
0
        return (mStreams.size());
91
0
    }
92
93
private:
94
    mutable std::mutex mLock;
95
    std::vector<TraceStream *> mStreams;
96
};
97
98
TraceOutput gTraceOutputs;
99
100
std::string AsJsonKey(const std::string & key, const std::string & value, bool prepend_comma)
101
0
{
102
0
    return (prepend_comma ? ", "s : ""s) + "\""s + key + "\": " + value;
103
0
}
104
105
std::string AsFirstJsonKey(const std::string & key, const std::string & value)
106
0
{
107
0
    return AsJsonKey(key, value, /* prepend_comma = */ false);
108
0
}
109
110
std::string AsNextJsonKey(const std::string & key, const std::string & value)
111
0
{
112
0
    return AsJsonKey(key, value, /* prepend_comma = */ true);
113
0
}
114
115
std::string AsJsonString(const std::string & value)
116
0
{
117
0
    return "\""s + value + "\""s;
118
0
}
119
120
std::string AsJsonString(const Transport::PeerAddress * peerAddress)
121
0
{
122
0
    char convBuf[Transport::PeerAddress::kMaxToStringSize] = { 0 };
123
124
0
    peerAddress->ToString(convBuf);
125
0
    return AsJsonString(convBuf);
126
0
}
127
128
std::string AsJsonBool(bool isTrue)
129
0
{
130
0
    return isTrue ? "true"s : "false"s;
131
0
}
132
133
std::string AsJsonHexString(const uint8_t * buf, size_t bufLen)
134
0
{
135
    // Avoid hex conversion that would fail on empty
136
0
    if (bufLen == 0)
137
0
    {
138
0
        return AsJsonString("");
139
0
    }
140
141
    // Fill a buffer long enough for the hex conversion, that will be overwritten
142
0
    std::vector<char> hexBuf(2 * bufLen, '\0');
143
144
0
    CHIP_ERROR status = Encoding::BytesToLowercaseHexBuffer(buf, bufLen, hexBuf.data(), hexBuf.size());
145
146
    // Static conditions exist that should ensure never failing. Catch failure in an assert.
147
0
    if (status != CHIP_NO_ERROR)
148
0
    {
149
0
        ChipLogError(Support, "Unexpected failure: %" CHIP_ERROR_FORMAT, status.Format());
150
0
        VerifyOrDie(status == CHIP_NO_ERROR);
151
0
    }
152
153
0
    return AsJsonString(std::string(hexBuf.data(), hexBuf.size()));
154
0
}
155
156
std::string PacketHeaderToJson(const PacketHeader * packetHeader)
157
0
{
158
0
    std::string jsonBody;
159
160
0
    uint32_t messageCounter = packetHeader->GetMessageCounter();
161
0
    jsonBody += AsFirstJsonKey("msg_counter", std::to_string(messageCounter));
162
163
0
    const Optional<NodeId> & sourceNodeId = packetHeader->GetSourceNodeId();
164
0
    if (sourceNodeId.HasValue())
165
0
    {
166
0
        jsonBody += AsNextJsonKey("source_node_id", std::to_string(sourceNodeId.Value()));
167
0
    }
168
169
0
    uint16_t sessionId                  = packetHeader->GetSessionId();
170
0
    const Optional<GroupId> & groupId   = packetHeader->GetDestinationGroupId();
171
0
    const Optional<NodeId> & destNodeId = packetHeader->GetDestinationNodeId();
172
0
    if (packetHeader->IsValidGroupMsg())
173
0
    {
174
0
        if (groupId.HasValue())
175
0
        {
176
0
            jsonBody += AsNextJsonKey("group_id", std::to_string(groupId.Value()));
177
0
        }
178
179
0
        jsonBody += AsNextJsonKey("group_key_hash", std::to_string(sessionId));
180
0
    }
181
0
    else if (destNodeId.HasValue())
182
0
    {
183
0
        jsonBody += AsNextJsonKey("dest_node_id", std::to_string(destNodeId.Value()));
184
0
    }
185
186
0
    jsonBody += AsNextJsonKey("session_id", std::to_string(sessionId));
187
188
0
    uint8_t messageFlags = packetHeader->GetMessageFlags();
189
0
    jsonBody += AsNextJsonKey("msg_flags", std::to_string(messageFlags));
190
191
0
    uint8_t securityFlags = packetHeader->GetSecurityFlags();
192
0
    jsonBody += AsNextJsonKey("security_flags", std::to_string(securityFlags));
193
194
0
    return jsonBody;
195
0
}
196
197
std::string PayloadHeaderToJson(const PayloadHeader * payloadHeader)
198
0
{
199
200
0
    std::string jsonBody;
201
202
0
    uint8_t exchangeFlags = payloadHeader->GetExchangeFlags();
203
0
    jsonBody += AsFirstJsonKey("exchange_flags", std::to_string(exchangeFlags));
204
205
0
    uint16_t exchangeId = payloadHeader->GetExchangeID();
206
0
    jsonBody += AsNextJsonKey("exchange_id", std::to_string(exchangeId));
207
208
0
    uint32_t protocolId = payloadHeader->GetProtocolID().ToFullyQualifiedSpecForm();
209
0
    jsonBody += AsNextJsonKey("protocol_id", std::to_string(protocolId));
210
211
0
    uint8_t protocolOpcode = payloadHeader->GetMessageType();
212
0
    jsonBody += AsNextJsonKey("protocol_opcode", std::to_string(protocolOpcode));
213
214
0
    bool isInitiator = payloadHeader->IsInitiator();
215
0
    jsonBody += AsNextJsonKey("is_initiator", AsJsonBool(isInitiator));
216
217
0
    bool needsAck = payloadHeader->NeedsAck();
218
0
    jsonBody += AsNextJsonKey("is_ack_requested", AsJsonBool(needsAck));
219
220
0
    const Optional<uint32_t> & acknowledgedMessageCounter = payloadHeader->GetAckMessageCounter();
221
0
    if (acknowledgedMessageCounter.HasValue())
222
0
    {
223
0
        jsonBody += AsNextJsonKey("acknowledged_msg_counter", std::to_string(acknowledgedMessageCounter.Value()));
224
0
    }
225
226
0
    return jsonBody;
227
0
}
228
229
void SecureMessageSentHandler(const TraceSecureMessageSentData * eventData)
230
0
{
231
0
    if (!gTraceOutputs.HasStreamAvailable())
232
0
    {
233
0
        return;
234
0
    }
235
236
0
    std::string jsonBody = "{ \"direction\": \"outbound\", ";
237
0
    jsonBody += AsFirstJsonKey("peer_address", AsJsonString(eventData->peerAddress));
238
0
    jsonBody += ", ";
239
0
    jsonBody += PacketHeaderToJson(eventData->packetHeader);
240
0
    jsonBody += ", ";
241
0
    jsonBody += PayloadHeaderToJson(eventData->payloadHeader);
242
0
    jsonBody += ", ";
243
0
    jsonBody += AsFirstJsonKey("payload_size", std::to_string(eventData->packetSize));
244
0
    jsonBody += ", ";
245
0
    jsonBody += AsFirstJsonKey("payload_hex", AsJsonHexString(eventData->packetPayload, eventData->packetSize));
246
0
    jsonBody += "}";
247
248
0
    gTraceOutputs.StartEvent(std::string{ kTraceMessageEvent } + "." + kTraceMessageSentDataFormat);
249
0
    gTraceOutputs.AddField("json", jsonBody);
250
0
    gTraceOutputs.FinishEvent();
251
0
}
252
253
void SecureMessageReceivedHandler(const TraceSecureMessageReceivedData * eventData)
254
0
{
255
0
    if (!gTraceOutputs.HasStreamAvailable())
256
0
    {
257
0
        return;
258
0
    }
259
260
0
    std::string jsonBody = "{ \"direction\": \"inbound\", ";
261
0
    jsonBody += AsFirstJsonKey("peer_address", AsJsonString(eventData->peerAddress));
262
0
    jsonBody += ", ";
263
0
    jsonBody += PacketHeaderToJson(eventData->packetHeader);
264
0
    jsonBody += ", ";
265
0
    jsonBody += PayloadHeaderToJson(eventData->payloadHeader);
266
0
    jsonBody += ", ";
267
0
    jsonBody += AsFirstJsonKey("payload_size", std::to_string(eventData->packetSize));
268
0
    jsonBody += ", ";
269
0
    jsonBody += AsFirstJsonKey("payload_hex", AsJsonHexString(eventData->packetPayload, eventData->packetSize));
270
0
    jsonBody += "}";
271
272
0
    gTraceOutputs.StartEvent(std::string{ kTraceMessageEvent } + "." + kTraceMessageReceivedDataFormat);
273
0
    gTraceOutputs.AddField("json", jsonBody);
274
0
    gTraceOutputs.FinishEvent();
275
276
    // Note that `eventData->session` is currently ignored.
277
0
}
278
279
void TraceHandler(const char * type, const void * data, size_t size)
280
0
{
281
0
    if ((std::string{ type } == kTraceMessageSentDataFormat) && (size == sizeof(TraceSecureMessageSentData)))
282
0
    {
283
0
        SecureMessageSentHandler(reinterpret_cast<const TraceSecureMessageSentData *>(data));
284
0
    }
285
0
    else if ((std::string{ type } == kTraceMessageReceivedDataFormat) && (size == sizeof(TraceSecureMessageReceivedData)))
286
0
    {
287
0
        SecureMessageReceivedHandler(reinterpret_cast<const TraceSecureMessageReceivedData *>(data));
288
0
    }
289
0
}
290
291
} // namespace
292
293
void AddTraceStream(TraceStream * stream)
294
0
{
295
0
    gTraceOutputs.RegisterStream(stream);
296
0
}
297
298
void InitTrace()
299
0
{
300
0
    SetTransportTraceHook(TraceHandler);
301
0
}
302
303
void DeInitTrace()
304
0
{
305
0
    gTraceOutputs.UnregisterAllStreams();
306
0
}
307
308
#else
309
void AddTraceStream(TraceStream *) {}
310
void InitTrace() {}
311
void DeInitTrace() {}
312
#endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
313
314
} // namespace trace
315
} // namespace chip