1 /*
2 * Copyright (C) 2023 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 #define LOG_TAG "NetworkTrace"
18 #define ATRACE_TAG ATRACE_TAG_NETWORK
19
20 #include "netdbpf/NetworkTracePoller.h"
21
22 #include <bpf/BpfUtils.h>
23 #include <cutils/trace.h>
24 #include <log/log.h>
25 #include <perfetto/tracing/platform.h>
26 #include <perfetto/tracing/tracing.h>
27
28 #include <unordered_map>
29 #include <unordered_set>
30
31 #include "netdbpf/BpfNetworkStats.h"
32
33 namespace android {
34 namespace bpf {
35 namespace internal {
36 using ::android::base::StringPrintf;
37
PollAndSchedule(perfetto::base::TaskRunner * runner,uint32_t poll_ms)38 void NetworkTracePoller::PollAndSchedule(perfetto::base::TaskRunner* runner,
39 uint32_t poll_ms) {
40 // Always schedule another run of ourselves to recursively poll periodically.
41 // The task runner is sequential so these can't run on top of each other.
42 runner->PostDelayedTask([=]() { PollAndSchedule(runner, poll_ms); }, poll_ms);
43
44 if (mMutex.try_lock()) {
45 ConsumeAllLocked();
46 mMutex.unlock();
47 }
48 }
49
Start(uint32_t pollMs)50 bool NetworkTracePoller::Start(uint32_t pollMs) {
51 ALOGD("Starting datasource");
52
53 std::scoped_lock<std::mutex> lock(mMutex);
54 if (mSessionCount > 0) {
55 if (mPollMs != pollMs) {
56 // Nothing technical prevents mPollMs from changing, it's just unclear
57 // what the right behavior is. Taking the min of active values could poll
58 // too frequently giving some sessions too much data. Taking the max could
59 // be too infrequent. For now, do nothing.
60 ALOGI("poll_ms can't be changed while running, ignoring poll_ms=%d",
61 pollMs);
62 }
63 mSessionCount++;
64 return true;
65 }
66
67 auto status = mConfigurationMap.init(PACKET_TRACE_ENABLED_MAP_PATH);
68 if (!status.ok()) {
69 ALOGW("Failed to bind config map: %s", status.error().message().c_str());
70 return false;
71 }
72
73 auto rb = BpfRingbuf<PacketTrace>::Create(PACKET_TRACE_RINGBUF_PATH);
74 if (!rb.ok()) {
75 ALOGW("Failed to create ringbuf: %s", rb.error().message().c_str());
76 return false;
77 }
78
79 mRingBuffer = std::move(*rb);
80
81 auto res = mConfigurationMap.writeValue(0, true, BPF_ANY);
82 if (!res.ok()) {
83 ALOGW("Failed to enable tracing: %s", res.error().message().c_str());
84 return false;
85 }
86
87 // Start a task runner to run ConsumeAll every mPollMs milliseconds.
88 mTaskRunner = perfetto::Platform::GetDefaultPlatform()->CreateTaskRunner({});
89 mPollMs = pollMs;
90 PollAndSchedule(mTaskRunner.get(), mPollMs);
91
92 mSessionCount++;
93 return true;
94 }
95
Stop()96 bool NetworkTracePoller::Stop() {
97 ALOGD("Stopping datasource");
98
99 std::scoped_lock<std::mutex> lock(mMutex);
100 if (mSessionCount == 0) return false; // This should never happen
101
102 // If this isn't the last session, don't clean up yet.
103 if (--mSessionCount > 0) return true;
104
105 auto res = mConfigurationMap.writeValue(0, false, BPF_ANY);
106 if (!res.ok()) {
107 ALOGW("Failed to disable tracing: %s", res.error().message().c_str());
108 }
109
110 // Make sure everything in the system has actually seen the 'false' we just
111 // wrote, things should now be well and truly disabled.
112 synchronizeKernelRCU();
113
114 // Drain remaining events from the ring buffer now that tracing is disabled.
115 // This prevents the next trace from seeing stale events and allows writing
116 // the last batch of events to Perfetto.
117 ConsumeAllLocked();
118
119 mTaskRunner.reset();
120 mRingBuffer.reset();
121
122 return res.ok();
123 }
124
TraceIfaces(const std::vector<PacketTrace> & packets)125 void NetworkTracePoller::TraceIfaces(const std::vector<PacketTrace>& packets) {
126 if (packets.empty()) return;
127
128 std::unordered_set<uint32_t> uniqueIfindex;
129 for (const PacketTrace& pkt : packets) {
130 uniqueIfindex.insert(pkt.ifindex);
131 }
132
133 for (uint32_t ifindex : uniqueIfindex) {
134 char ifname[IF_NAMESIZE] = {};
135 if (if_indextoname(ifindex, ifname) != ifname) continue;
136
137 StatsValue stats = {};
138 if (bpfGetIfIndexStats(ifindex, &stats) != 0) continue;
139
140 std::string rxTrack = StringPrintf("%s [%d] Rx Bytes", ifname, ifindex);
141 std::string txTrack = StringPrintf("%s [%d] Tx Bytes", ifname, ifindex);
142 ATRACE_INT64(rxTrack.c_str(), stats.rxBytes);
143 ATRACE_INT64(txTrack.c_str(), stats.txBytes);
144 }
145 }
146
ConsumeAll()147 bool NetworkTracePoller::ConsumeAll() {
148 std::scoped_lock<std::mutex> lock(mMutex);
149 return ConsumeAllLocked();
150 }
151
ConsumeAllLocked()152 bool NetworkTracePoller::ConsumeAllLocked() {
153 if (mRingBuffer == nullptr) {
154 ALOGW("Tracing is not active");
155 return false;
156 }
157
158 std::vector<PacketTrace> packets;
159 base::Result<int> ret = mRingBuffer->ConsumeAll(
160 [&](const PacketTrace& pkt) { packets.push_back(pkt); });
161 if (!ret.ok()) {
162 ALOGW("Failed to poll ringbuf: %s", ret.error().message().c_str());
163 return false;
164 }
165
166 ATRACE_INT("NetworkTracePackets", packets.size());
167
168 TraceIfaces(packets);
169 mCallback(packets);
170
171 return true;
172 }
173
174 } // namespace internal
175 } // namespace bpf
176 } // namespace android
177