Merge "Implement puller for new SurfaceFlinger dmabuf stats." into tm-dev

This commit is contained in:
Richard Gaywood
2022-03-17 18:04:18 +00:00
committed by Android (Google) Code Review
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

@@ -2345,51 +2345,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;
}