Implement puller for new SurfaceFlinger dmabuf stats.

Involves pushing most of the logic down out of StatsPullAtomService
and into KernelAllocationStats.cpp, as well as changing some method
signatures along the way.

At the end of this work, two new fields in atoms.proto will be
populated; these will allow us to tell how much of SurfaceFlinger's
dmabuf memory usage is due to buffers shared with other processes
and how much is due to private internal buffers.

Test: statsd_testdrive 10105
Test: adb shell dmabuf_dump
Bug: 220084049
Change-Id: Ia41cfd693d3db2bd06083801075cae290f67221f
This commit is contained in:
Richard Gaywood
2022-03-15 20:37:58 +00:00
parent 379a87059e
commit 3be5773ec9
4 changed files with 163 additions and 75 deletions

View File

@@ -24,21 +24,29 @@ public final class KernelAllocationStats {
/** Process dma-buf stats. */
public static final class ProcessDmabuf {
public final int uid;
public final String processName;
public final int oomScore;
/** Size of buffers retained by the process. */
public final int retainedSizeKb;
/** Number of buffers retained by the process. */
public final int retainedBuffersCount;
/** Size of buffers mapped to the address space. */
public final int mappedSizeKb;
/** Count of buffers mapped to the address space. */
public final int mappedBuffersCount;
/** Size of buffers shared with Surface Flinger. */
public final int surfaceFlingerSizeKb;
/** Count of buffers shared with Surface Flinger. */
public final int surfaceFlingerCount;
ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount,
int mappedSizeKb, int mappedBuffersCount) {
ProcessDmabuf(int uid, String processName, int oomScore, int retainedSizeKb,
int retainedBuffersCount, int surfaceFlingerSizeKb,
int surfaceFlingerCount) {
this.uid = uid;
this.processName = processName;
this.oomScore = oomScore;
this.retainedSizeKb = retainedSizeKb;
this.retainedBuffersCount = retainedBuffersCount;
this.mappedSizeKb = mappedSizeKb;
this.mappedBuffersCount = mappedBuffersCount;
this.surfaceFlingerSizeKb = surfaceFlingerSizeKb;
this.surfaceFlingerCount = surfaceFlingerCount;
}
}
@@ -47,7 +55,7 @@ public final class KernelAllocationStats {
* stats could not be read.
*/
@Nullable
public static native ProcessDmabuf getDmabufAllocations(int pid);
public static native ProcessDmabuf[] getDmabufAllocations();
/** Pid to gpu memory size. */
public static final class ProcessGpuMem {

View File

@@ -272,6 +272,7 @@ cc_library_shared {
"libinput",
"libcamera_client",
"libcamera_metadata",
"libprocinfo",
"libsqlite",
"libEGL",
"libGLESv1_CM",

View File

@@ -13,12 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <dmabufinfo/dmabufinfo.h>
#include <jni.h>
#include <meminfo/sysmeminfo.h>
#include <procinfo/process.h>
#include "core_jni_helpers.h"
using DmaBuffer = ::android::dmabufinfo::DmaBuffer;
using android::base::ReadFileToString;
using android::base::StringPrintf;
namespace {
static jclass gProcessDmabufClazz;
static jmethodID gProcessDmabufCtor;
@@ -28,30 +35,127 @@ static jmethodID gProcessGpuMemCtor;
namespace android {
static jobject KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject, jint pid) {
std::vector<dmabufinfo::DmaBuffer> buffers;
if (!dmabufinfo::ReadDmaBufMapRefs(pid, &buffers)) {
struct PidDmaInfo {
uid_t uid;
std::string cmdline;
int oomScoreAdj;
};
static jobjectArray KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject) {
std::vector<DmaBuffer> buffers;
if (!dmabufinfo::ReadDmaBufs(&buffers)) {
return nullptr;
}
jint mappedSize = 0;
jint mappedCount = buffers.size();
for (const auto &buffer : buffers) {
mappedSize += buffer.size();
}
mappedSize /= 1024;
jint retainedSize = -1;
jint retainedCount = -1;
if (dmabufinfo::ReadDmaBufFdRefs(pid, &buffers)) {
retainedCount = buffers.size();
retainedSize = 0;
for (const auto &buffer : buffers) {
retainedSize += buffer.size();
// Create a reverse map from pid to dmabufs
// Store dmabuf inodes & sizes for later processing.
std::unordered_map<pid_t, std::set<ino_t>> pidToInodes;
std::unordered_map<ino_t, long> inodeToSize;
for (auto &buf : buffers) {
for (auto pid : buf.pids()) {
pidToInodes[pid].insert(buf.inode());
}
retainedSize /= 1024;
inodeToSize[buf.inode()] = buf.size();
}
return env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, retainedSize, retainedCount,
mappedSize, mappedCount);
pid_t surfaceFlingerPid = -1;
// The set of all inodes that are being retained by SurfaceFlinger. Buffers
// shared between another process and SF will appear in this set.
std::set<ino_t> surfaceFlingerBufferInodes;
// The set of all inodes that are being retained by any process other
// than SurfaceFlinger. Buffers shared between another process and SF will
// appear in this set.
std::set<ino_t> otherProcessBufferInodes;
// Find SurfaceFlinger pid & get cmdlines, oomScoreAdj, etc for each pid
// holding any DMA buffers.
std::unordered_map<pid_t, PidDmaInfo> pidDmaInfos;
for (const auto &pidToInodeEntry : pidToInodes) {
pid_t pid = pidToInodeEntry.first;
android::procinfo::ProcessInfo processInfo;
if (!android::procinfo::GetProcessInfo(pid, &processInfo)) {
continue;
}
std::string cmdline;
if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) {
continue;
}
// cmdline strings are null-delimited, so we split on \0 here
if (cmdline.substr(0, cmdline.find('\0')) == "/system/bin/surfaceflinger") {
if (surfaceFlingerPid == -1) {
surfaceFlingerPid = pid;
surfaceFlingerBufferInodes = pidToInodes[pid];
} else {
LOG(ERROR) << "getDmabufAllocations found multiple SF processes; pid1: " << pid
<< ", pid2:" << surfaceFlingerPid;
surfaceFlingerPid = -2; // Used as a sentinel value below
}
} else {
otherProcessBufferInodes.insert(pidToInodes[pid].begin(), pidToInodes[pid].end());
}
std::string oomScoreAdjStr;
if (!ReadFileToString(StringPrintf("/proc/%d/oom_score_adj", pid), &oomScoreAdjStr)) {
continue;
}
pidDmaInfos[pid] = PidDmaInfo{.uid = processInfo.uid,
.cmdline = cmdline,
.oomScoreAdj = atoi(oomScoreAdjStr.c_str())};
}
if (surfaceFlingerPid < 0) {
LOG(ERROR) << "getDmabufAllocations could not identify SurfaceFlinger "
<< "process via /proc/pid/cmdline";
}
jobjectArray ret = env->NewObjectArray(pidDmaInfos.size(), gProcessDmabufClazz, NULL);
int retArrayIndex = 0;
for (const auto &pidDmaInfosEntry : pidDmaInfos) {
pid_t pid = pidDmaInfosEntry.first;
// For all processes apart from SurfaceFlinger, this set will store the
// dmabuf inodes that are shared with SF. For SF, it will store the inodes
// that are shared with any other process.
std::set<ino_t> sharedBuffers;
if (pid == surfaceFlingerPid) {
set_intersection(surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(),
otherProcessBufferInodes.begin(), otherProcessBufferInodes.end(),
std::inserter(sharedBuffers, sharedBuffers.end()));
} else if (surfaceFlingerPid > 0) {
set_intersection(pidToInodes[pid].begin(), pidToInodes[pid].end(),
surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(),
std::inserter(sharedBuffers, sharedBuffers.begin()));
} // If surfaceFlingerPid < 0; it means we failed to identify it, and
// the SF-related fields below should be left empty.
long totalSize = 0;
long sharedBuffersSize = 0;
for (const auto &inode : pidToInodes[pid]) {
totalSize += inodeToSize[inode];
if (sharedBuffers.count(inode)) {
sharedBuffersSize += inodeToSize[inode];
}
}
jobject obj = env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor,
/* uid */ pidDmaInfos[pid].uid,
/* process name */
env->NewStringUTF(pidDmaInfos[pid].cmdline.c_str()),
/* oomscore */ pidDmaInfos[pid].oomScoreAdj,
/* retainedSize */ totalSize / 1024,
/* retainedCount */ pidToInodes[pid].size(),
/* sharedWithSurfaceFlinger size */ sharedBuffersSize / 1024,
/* sharedWithSurfaceFlinger count */ sharedBuffers.size());
env->SetObjectArrayElement(ret, retArrayIndex++, obj);
}
return ret;
}
static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) {
@@ -74,7 +178,7 @@ static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) {
}
static const JNINativeMethod methods[] = {
{"getDmabufAllocations", "(I)Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;",
{"getDmabufAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;",
(void *)KernelAllocationStats_getDmabufAllocations},
{"getGpuAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessGpuMem;",
(void *)KernelAllocationStats_getGpuAllocations},
@@ -86,7 +190,8 @@ int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) {
jclass clazz =
FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessDmabuf");
gProcessDmabufClazz = MakeGlobalRefOrDie(env, clazz);
gProcessDmabufCtor = GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(IIII)V");
gProcessDmabufCtor =
GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(ILjava/lang/String;IIIII)V");
clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessGpuMem");
gProcessGpuMemClazz = MakeGlobalRefOrDie(env, clazz);
@@ -94,4 +199,4 @@ int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) {
return res;
}
} // namespace android
} // namespace android

View File

@@ -2338,51 +2338,25 @@ public class StatsPullAtomService extends SystemService {
}
int pullProcessDmabufMemory(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> managedProcessList =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
for (ProcessMemoryState process : managedProcessList) {
KernelAllocationStats.ProcessDmabuf proc =
KernelAllocationStats.getDmabufAllocations(process.pid);
if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
continue;
}
pulledData.add(
FrameworkStatsLog.buildStatsEvent(
atomTag,
process.uid,
process.processName,
process.oomScore,
proc.retainedSizeKb,
proc.retainedBuffersCount,
proc.mappedSizeKb,
proc.mappedBuffersCount));
KernelAllocationStats.ProcessDmabuf[] procBufs =
KernelAllocationStats.getDmabufAllocations();
if (procBufs == null) {
return StatsManager.PULL_SKIP;
}
SparseArray<String> processCmdlines = getProcessCmdlines();
managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
int size = processCmdlines.size();
for (int i = 0; i < size; ++i) {
int pid = processCmdlines.keyAt(i);
int uid = getUidForPid(pid);
// ignore root processes (unlikely to be interesting)
if (uid <= 0) {
continue;
}
KernelAllocationStats.ProcessDmabuf proc =
KernelAllocationStats.getDmabufAllocations(pid);
if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
continue;
}
pulledData.add(
FrameworkStatsLog.buildStatsEvent(
atomTag,
uid,
processCmdlines.valueAt(i),
-1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/,
proc.retainedSizeKb,
proc.retainedBuffersCount,
proc.mappedSizeKb,
proc.mappedBuffersCount));
for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
atomTag,
procBuf.uid,
procBuf.processName,
procBuf.oomScore,
procBuf.retainedSizeKb,
procBuf.retainedBuffersCount,
0, /* mapped_dmabuf_kb - deprecated */
0, /* mapped_dmabuf_count - deprecated */
procBuf.surfaceFlingerSizeKb,
procBuf.surfaceFlingerCount
));
}
return StatsManager.PULL_SUCCESS;
}