1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2020 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #undef LOG_TAG
18*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "GpuMem"
19*38e8c45fSAndroid Build Coastguard Worker #define ATRACE_TAG ATRACE_TAG_GRAPHICS
20*38e8c45fSAndroid Build Coastguard Worker
21*38e8c45fSAndroid Build Coastguard Worker #include "gpumem/GpuMem.h"
22*38e8c45fSAndroid Build Coastguard Worker
23*38e8c45fSAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
24*38e8c45fSAndroid Build Coastguard Worker #include <libbpf.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <bpf/WaitForProgsLoaded.h>
26*38e8c45fSAndroid Build Coastguard Worker #include <log/log.h>
27*38e8c45fSAndroid Build Coastguard Worker #include <unistd.h>
28*38e8c45fSAndroid Build Coastguard Worker #include <utils/Timers.h>
29*38e8c45fSAndroid Build Coastguard Worker #include <utils/Trace.h>
30*38e8c45fSAndroid Build Coastguard Worker
31*38e8c45fSAndroid Build Coastguard Worker #include <unordered_map>
32*38e8c45fSAndroid Build Coastguard Worker #include <vector>
33*38e8c45fSAndroid Build Coastguard Worker
34*38e8c45fSAndroid Build Coastguard Worker namespace android {
35*38e8c45fSAndroid Build Coastguard Worker
36*38e8c45fSAndroid Build Coastguard Worker using base::StringAppendF;
37*38e8c45fSAndroid Build Coastguard Worker
~GpuMem()38*38e8c45fSAndroid Build Coastguard Worker GpuMem::~GpuMem() {
39*38e8c45fSAndroid Build Coastguard Worker bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
40*38e8c45fSAndroid Build Coastguard Worker }
41*38e8c45fSAndroid Build Coastguard Worker
initialize()42*38e8c45fSAndroid Build Coastguard Worker void GpuMem::initialize() {
43*38e8c45fSAndroid Build Coastguard Worker // Make sure bpf programs are loaded
44*38e8c45fSAndroid Build Coastguard Worker bpf::waitForProgsLoaded();
45*38e8c45fSAndroid Build Coastguard Worker
46*38e8c45fSAndroid Build Coastguard Worker errno = 0;
47*38e8c45fSAndroid Build Coastguard Worker int fd = bpf::retrieveProgram(kGpuMemTotalProgPath);
48*38e8c45fSAndroid Build Coastguard Worker if (fd < 0) {
49*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kGpuMemTotalProgPath, errno,
50*38e8c45fSAndroid Build Coastguard Worker strerror(errno));
51*38e8c45fSAndroid Build Coastguard Worker return;
52*38e8c45fSAndroid Build Coastguard Worker }
53*38e8c45fSAndroid Build Coastguard Worker
54*38e8c45fSAndroid Build Coastguard Worker // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
55*38e8c45fSAndroid Build Coastguard Worker errno = 0;
56*38e8c45fSAndroid Build Coastguard Worker int count = 0;
57*38e8c45fSAndroid Build Coastguard Worker while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
58*38e8c45fSAndroid Build Coastguard Worker if (++count > kGpuWaitTimeout) {
59*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kGpuMemTraceGroup,
60*38e8c45fSAndroid Build Coastguard Worker kGpuMemTotalTracepoint, errno, strerror(errno));
61*38e8c45fSAndroid Build Coastguard Worker return;
62*38e8c45fSAndroid Build Coastguard Worker }
63*38e8c45fSAndroid Build Coastguard Worker // Retry until GPU driver loaded or timeout.
64*38e8c45fSAndroid Build Coastguard Worker if (mStop.load()) return;
65*38e8c45fSAndroid Build Coastguard Worker sleep(1);
66*38e8c45fSAndroid Build Coastguard Worker }
67*38e8c45fSAndroid Build Coastguard Worker
68*38e8c45fSAndroid Build Coastguard Worker // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
69*38e8c45fSAndroid Build Coastguard Worker errno = 0;
70*38e8c45fSAndroid Build Coastguard Worker auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
71*38e8c45fSAndroid Build Coastguard Worker if (!map.isValid()) {
72*38e8c45fSAndroid Build Coastguard Worker ALOGE("Failed to create bpf map from %s [%d(%s)]", kGpuMemTotalMapPath, errno,
73*38e8c45fSAndroid Build Coastguard Worker strerror(errno));
74*38e8c45fSAndroid Build Coastguard Worker return;
75*38e8c45fSAndroid Build Coastguard Worker }
76*38e8c45fSAndroid Build Coastguard Worker setGpuMemTotalMap(map);
77*38e8c45fSAndroid Build Coastguard Worker
78*38e8c45fSAndroid Build Coastguard Worker mInitialized.store(true);
79*38e8c45fSAndroid Build Coastguard Worker }
80*38e8c45fSAndroid Build Coastguard Worker
setGpuMemTotalMap(bpf::BpfMapRO<uint64_t,uint64_t> & map)81*38e8c45fSAndroid Build Coastguard Worker void GpuMem::setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map) {
82*38e8c45fSAndroid Build Coastguard Worker mGpuMemTotalMap = std::move(map);
83*38e8c45fSAndroid Build Coastguard Worker }
84*38e8c45fSAndroid Build Coastguard Worker
85*38e8c45fSAndroid Build Coastguard Worker // Dump the snapshots of global and per process memory usage on all gpus
dump(const Vector<String16> &,std::string * result)86*38e8c45fSAndroid Build Coastguard Worker void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
87*38e8c45fSAndroid Build Coastguard Worker ATRACE_CALL();
88*38e8c45fSAndroid Build Coastguard Worker
89*38e8c45fSAndroid Build Coastguard Worker if (!mInitialized.load() || !mGpuMemTotalMap.isValid()) {
90*38e8c45fSAndroid Build Coastguard Worker result->append("Failed to initialize GPU memory eBPF\n");
91*38e8c45fSAndroid Build Coastguard Worker return;
92*38e8c45fSAndroid Build Coastguard Worker }
93*38e8c45fSAndroid Build Coastguard Worker
94*38e8c45fSAndroid Build Coastguard Worker auto res = mGpuMemTotalMap.getFirstKey();
95*38e8c45fSAndroid Build Coastguard Worker if (!res.ok()) {
96*38e8c45fSAndroid Build Coastguard Worker result->append("GPU memory total usage map is empty\n");
97*38e8c45fSAndroid Build Coastguard Worker return;
98*38e8c45fSAndroid Build Coastguard Worker }
99*38e8c45fSAndroid Build Coastguard Worker uint64_t key = res.value();
100*38e8c45fSAndroid Build Coastguard Worker // unordered_map<gpu_id, vector<pair<pid, size>>>
101*38e8c45fSAndroid Build Coastguard Worker std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
102*38e8c45fSAndroid Build Coastguard Worker while (true) {
103*38e8c45fSAndroid Build Coastguard Worker uint32_t gpu_id = key >> 32;
104*38e8c45fSAndroid Build Coastguard Worker uint32_t pid = key;
105*38e8c45fSAndroid Build Coastguard Worker
106*38e8c45fSAndroid Build Coastguard Worker res = mGpuMemTotalMap.readValue(key);
107*38e8c45fSAndroid Build Coastguard Worker if (!res.ok()) break;
108*38e8c45fSAndroid Build Coastguard Worker uint64_t size = res.value();
109*38e8c45fSAndroid Build Coastguard Worker
110*38e8c45fSAndroid Build Coastguard Worker dumpMap[gpu_id].emplace_back(pid, size);
111*38e8c45fSAndroid Build Coastguard Worker
112*38e8c45fSAndroid Build Coastguard Worker res = mGpuMemTotalMap.getNextKey(key);
113*38e8c45fSAndroid Build Coastguard Worker if (!res.ok()) break;
114*38e8c45fSAndroid Build Coastguard Worker key = res.value();
115*38e8c45fSAndroid Build Coastguard Worker }
116*38e8c45fSAndroid Build Coastguard Worker
117*38e8c45fSAndroid Build Coastguard Worker for (auto& gpu : dumpMap) {
118*38e8c45fSAndroid Build Coastguard Worker if (gpu.second.empty()) continue;
119*38e8c45fSAndroid Build Coastguard Worker StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
120*38e8c45fSAndroid Build Coastguard Worker
121*38e8c45fSAndroid Build Coastguard Worker std::sort(gpu.second.begin(), gpu.second.end(),
122*38e8c45fSAndroid Build Coastguard Worker [](auto& l, auto& r) { return l.first < r.first; });
123*38e8c45fSAndroid Build Coastguard Worker
124*38e8c45fSAndroid Build Coastguard Worker int i = 0;
125*38e8c45fSAndroid Build Coastguard Worker if (gpu.second[0].first != 0) {
126*38e8c45fSAndroid Build Coastguard Worker StringAppendF(result, "Global total: N/A\n");
127*38e8c45fSAndroid Build Coastguard Worker } else {
128*38e8c45fSAndroid Build Coastguard Worker StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
129*38e8c45fSAndroid Build Coastguard Worker i++;
130*38e8c45fSAndroid Build Coastguard Worker }
131*38e8c45fSAndroid Build Coastguard Worker for (; i < gpu.second.size(); i++) {
132*38e8c45fSAndroid Build Coastguard Worker StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
133*38e8c45fSAndroid Build Coastguard Worker gpu.second[i].second);
134*38e8c45fSAndroid Build Coastguard Worker }
135*38e8c45fSAndroid Build Coastguard Worker }
136*38e8c45fSAndroid Build Coastguard Worker }
137*38e8c45fSAndroid Build Coastguard Worker
traverseGpuMemTotals(const std::function<void (int64_t ts,uint32_t gpuId,uint32_t pid,uint64_t size)> & callback)138*38e8c45fSAndroid Build Coastguard Worker void GpuMem::traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
139*38e8c45fSAndroid Build Coastguard Worker uint64_t size)>& callback) {
140*38e8c45fSAndroid Build Coastguard Worker auto res = mGpuMemTotalMap.getFirstKey();
141*38e8c45fSAndroid Build Coastguard Worker if (!res.ok()) return;
142*38e8c45fSAndroid Build Coastguard Worker uint64_t key = res.value();
143*38e8c45fSAndroid Build Coastguard Worker while (true) {
144*38e8c45fSAndroid Build Coastguard Worker uint32_t gpu_id = key >> 32;
145*38e8c45fSAndroid Build Coastguard Worker uint32_t pid = key;
146*38e8c45fSAndroid Build Coastguard Worker
147*38e8c45fSAndroid Build Coastguard Worker res = mGpuMemTotalMap.readValue(key);
148*38e8c45fSAndroid Build Coastguard Worker if (!res.ok()) break;
149*38e8c45fSAndroid Build Coastguard Worker uint64_t size = res.value();
150*38e8c45fSAndroid Build Coastguard Worker
151*38e8c45fSAndroid Build Coastguard Worker callback(systemTime(), gpu_id, pid, size);
152*38e8c45fSAndroid Build Coastguard Worker res = mGpuMemTotalMap.getNextKey(key);
153*38e8c45fSAndroid Build Coastguard Worker if (!res.ok()) break;
154*38e8c45fSAndroid Build Coastguard Worker key = res.value();
155*38e8c45fSAndroid Build Coastguard Worker }
156*38e8c45fSAndroid Build Coastguard Worker }
157*38e8c45fSAndroid Build Coastguard Worker
158*38e8c45fSAndroid Build Coastguard Worker } // namespace android
159