blob: 928a788e459f110453fa309e8a704349381d668d [file] [log] [blame]
#include <iostream>
#include <errno.h>
#include <fstream>
#include <sstream>
#include <regex>
#include <list>
#include <algorithm>
#include <iomanip>
#include <map>
#include <dirent.h>
#include <cerrno>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <hardware/exynos/ion.h>
#define MAX_NAME_SIZE 64
using namespace std;
struct BufferNode;
struct ProcessNode;
struct mapNode {
mapNode(BufferNode *_bufnode, ProcessNode *_procnode)
: bufferNode(_bufnode), procNode(_procnode) {
refcount = fd = -1;
}
BufferNode *bufferNode;
ProcessNode *procNode;
int fd;
int refcount;
};
typedef map<int, ProcessNode> ProcessType;
typedef map<int, BufferNode> BufferType;
typedef map<int, mapNode> MapType;
struct BufferNode {
BufferNode(unsigned int _id, unsigned int _flags, size_t _size,
string _heaptype, string _heapname)
: id(_id), flags(_flags), fileCount(-1), size(_size), HeapType(_heaptype), HeapName(_heapname) { }
~BufferNode() { };
void setFileCount(int count)
{
fileCount = count;
}
void setAttachDevice(string device)
{
int offset = device.find_first_not_of(" \t\v\n");
attachedDevice.push_back(device.substr(offset));
}
void setTraceNode(unsigned int refcount, ProcessNode *procNode);
void setMapNode(unsigned int fd, ProcessNode *procNode);
string getIonFlag(void);
unsigned int id;
unsigned int flags;
int fileCount;
size_t size;
string HeapType;
string HeapName;
list<string> attachedDevice;
MapType mapList;
};
struct ProcessNode {
ProcessNode(unsigned int _pid)
: pid(_pid) {
setProcessName(_pid);
}
~ProcessNode() { };
void setProcessName(unsigned int pid);
void setTraceNode(unsigned int refcount, BufferNode *bufferNode);
void setMapNode(unsigned int fd, BufferNode *bufferNode);
unsigned int pid;
string comm;
MapType mapList;
};
class MapTable {
public:
MapTable()
: totalIonMemory(0)
{
};
~MapTable() { };
bool setupBuffer();
bool setupTraceInfo();
bool setupMapInfo();
ProcessNode *setupProcessNode(unsigned int pid);
BufferNode *getBufferNode(unsigned int buffer_id)
{
BufferType::iterator iter;
iter = bufferList.find(buffer_id);
if (iter != bufferList.end())
return &iter->second;
return nullptr;
}
ProcessNode *getProcessNode(unsigned int pid)
{
ProcessType::iterator iter;
iter = processList.find(pid);
if (iter != processList.end())
return &iter->second;
return nullptr;
}
void printBuffer(unsigned int buffer_id);
void printBuffer(BufferNode &bufferNode);
void printProcess(unsigned int pid);
void printProcess(ProcessNode &processNode);
void print();
void printSummary(void);
private:
unsigned int totalIonMemory;
BufferType bufferList;
ProcessType processList;
};
void BufferNode::setTraceNode(unsigned int refcount, ProcessNode *procNode)
{
MapType::iterator iter;
pair<MapType::iterator, bool> node;
iter = mapList.find(procNode->pid);
if (iter != mapList.end()) {
iter->second.refcount = refcount;
return;
}
node = mapList.emplace(procNode->pid, mapNode(this, procNode));
node.first->second.refcount = refcount;
}
void BufferNode::setMapNode(unsigned int fd, ProcessNode *procNode)
{
MapType::iterator iter;
pair<MapType::iterator, bool> node;
iter = mapList.find(procNode->pid);
if (iter != mapList.end()) {
iter->second.fd = fd;
return;
}
node = mapList.emplace(procNode->pid, mapNode(this, procNode));
node.first->second.fd = fd;
}
string BufferNode::getIonFlag(void)
{
string tag;
if (flags & ION_FLAG_CACHED)
tag.append("C");
if (flags & ION_FLAG_CACHED_NEEDS_SYNC)
tag.append("N");
if (flags & ION_FLAG_NOZEROED)
tag.append("z");
if (flags & ION_FLAG_PROTECTED)
tag.append("P");
if (flags & ION_FLAG_SYNC_FORCE)
tag.append("S");
if (flags & ION_FLAG_MAY_HWRENDER)
tag.append("H");
return tag;
}
void ProcessNode::setProcessName(unsigned int pid)
{
if (pid == 0) {
comm = "Kernel";
return;
}
ostringstream ostr;
ostr << "/proc/" << pid << "/comm";
ifstream processDirectory(ostr.str());
if (processDirectory && getline(processDirectory, comm))
return;
comm = "{{INVALID}}";
}
void ProcessNode::setTraceNode(unsigned int refcount, BufferNode *bufferNode)
{
MapType::iterator iter;
pair<MapType::iterator, bool> node;
iter = mapList.find(bufferNode->id);
if (iter != mapList.end()) {
iter->second.refcount = refcount;
return;
}
node = mapList.emplace(bufferNode->id, mapNode(bufferNode, this));
node.first->second.refcount = refcount;
}
void ProcessNode::setMapNode(unsigned int fd, BufferNode *bufferNode)
{
MapType::iterator iter;
pair<MapType::iterator, bool> node;
iter = mapList.find(bufferNode->id);
if (iter != mapList.end()) {
iter->second.fd = fd;
return;
}
node = mapList.emplace(bufferNode->id, mapNode(bufferNode, this));
node.first->second.fd = fd;
}
#define NUM_ION_BUFMAX 2048
const char ION_BUFFERS_PATH[] = "/sys/kernel/debug/ion/buffers";
const char ION_HEAPS_PATH[] = "/sys/kernel/debug/ion/heaps/";
const char ION_EVENT_PATH[] = "/sys/kernel/debug/ion/event";
static const char ION_DMABUF_PREFIX[] = "anon_inode:dmabuf_ion";
const char DMABUF_BUFINFO_PATH[] = "/sys/kernel/debug/dma_buf/bufinfo";
const char PROC_PATH[] = "/proc";
const char DMABUF_FOOTPRINT_PATH[] = "/sys/kernel/debug/dma_buf/footprint";
bool MapTable::setupBuffer()
{
ifstream ion(ION_BUFFERS_PATH);
if (!ion) {
cout << "Buffer path does not exist (" << ION_BUFFERS_PATH << ")" << endl;
return false;
}
// [ id] heap heaptype flags size(kb)
// [ 106] ion_system_heap system 0x40 16912
regex rexion_ion("\\[ *(\\d+)\\] +(\\w+)+ +(\\w+) +([x[:xdigit:]]+) +(\\d+).*");
smatch mch;
// 1-id, 2-heapname 3-heaptype, 3-flags, 4-size
for (string line; getline(ion, line); ) {
if (regex_match(line, mch, rexion_ion)) {
unsigned int id = stoul(mch[1]);
unsigned int flags = stoul(mch[4], 0, 16);
size_t len = stoul(mch[5]);
bufferList.emplace(id, BufferNode(id, flags, len * 1024, mch[3], mch[2]));
totalIonMemory += len;
}
}
ifstream dmabuf(DMABUF_BUFINFO_PATH);
if (!dmabuf) {
cout << "dmabuf path does not exist (" << DMABUF_BUFINFO_PATH << ")" << endl;
return false;
}
//Dma-buf Objects:
//size flags mode count exp_name
//00004096 00000002 00000007 00000002 ion-333
// Attcached Devices:
// 18500000.mali
//Total 1 devices attached
//
regex rexion_dmabuf("(\\d+)\\s+(\\d{8})\\s+(\\d{8})\\s+(\\d{8})\\s+ion\\-(\\d+).*");
for (string line; getline(dmabuf, line); ) {
// 1-size 2-flags 3-mode 4-count 5-id
if (regex_match(line, mch, rexion_dmabuf)) {
unsigned int file_count = stoul(mch[4]);
unsigned id = stoul(mch[5]);
BufferNode *bufferNode = getBufferNode(id);
if (!bufferNode)
continue;
bufferNode->setFileCount(file_count);
// Attached Devices:
getline(dmabuf, line);
while (getline(dmabuf, line)) {
// Total n devices attached
if (!line.compare(0, 5, "Total"))
break;
bufferNode->setAttachDevice(line);
}
}
}
return true;
}
bool MapTable::setupTraceInfo()
{
//Dma-buf-trace Objects:
// exp_name size share refcount
// ion-333 151552 151552 1
DIR *proc = opendir(DMABUF_FOOTPRINT_PATH);
if (!proc) {
cout << "Footprint path does not exist (" << DMABUF_FOOTPRINT_PATH << ")" << endl;
return false;
}
struct dirent *ProcessDirectory;
while ((ProcessDirectory = readdir(proc)) != NULL) {
char *strptr;
unsigned int pid = strtoul(ProcessDirectory->d_name, &strptr, 10);
if (strptr == ProcessDirectory->d_name)
continue;
/* file_directory : /d/dma_buf/footprint/<pid> */
ostringstream file_directory;
file_directory << DMABUF_FOOTPRINT_PATH << "/" << pid;
ifstream trace(file_directory.str().c_str());
if (!trace) {
closedir(proc);
cout << "Process file of footprint does not exist (" << file_directory.str() << ")" << endl;
return false;
}
smatch mch;
regex rexion_trace(" *ion\\-(\\d+) *(\\d+) *(\\d+) *(\\d+)");
ProcessNode *procNode = nullptr;
for (string line; getline(trace, line); ) {
// 1-id 2-size 3-share 4-refcount
if (regex_match(line, mch, rexion_trace)) {
unsigned int id = stoul(mch[1]);
unsigned int refcount = stoul(mch[4]);
BufferNode *bufferNode = getBufferNode(id);
if (!bufferNode)
continue;
if (!procNode)
procNode = setupProcessNode(pid);
bufferNode->setTraceNode(refcount, procNode);
procNode->setTraceNode(refcount, bufferNode);
}
}
}
closedir(proc);
return true;
}
bool MapTable::setupMapInfo()
{
DIR *proc = opendir(PROC_PATH);
if (!proc) {
cout << "Access Fail /proc" << endl;
return false;
}
struct dirent *ProcessDirectory;
/* ProcessDirectory : /proc/ */
while ((ProcessDirectory = readdir(proc)) != NULL) {
char *strptr;
unsigned int pid = strtoul(ProcessDirectory->d_name, &strptr, 10);
if ((strptr == ProcessDirectory->d_name) || (*strptr != '\0'))
continue;
/* fd_directory : /proc/<pid>/fd */
ostringstream fd_directory;
fd_directory << "/proc/" << pid << "/fd/";
ProcessNode *procNode = nullptr;
DIR *ProcessFileDirectory;
if ((ProcessFileDirectory = opendir(fd_directory.str().c_str())) != NULL) {
struct dirent *FileEntry;
while ((FileEntry = readdir(ProcessFileDirectory)) != NULL) {
unsigned int fd = strtoul(FileEntry->d_name, &strptr, 10);
if (strptr == FileEntry->d_name)
continue;
/* file_directory : /proc/<pid>/fd/<fd> */
ostringstream file_directory;
file_directory << fd_directory.str() << FileEntry->d_name;
char symbolFile[MAX_NAME_SIZE] = {0, };
if (readlink(file_directory.str().c_str(),
symbolFile, sizeof(symbolFile)) < 0)
continue;
if (!strncmp(symbolFile, ION_DMABUF_PREFIX, sizeof(ION_DMABUF_PREFIX) - 1)) {
char *prefixStart = symbolFile + sizeof(ION_DMABUF_PREFIX);
unsigned int buffer_id = strtoul(prefixStart, &strptr, 10);
if (strptr == prefixStart)
continue;
BufferNode *bufferNode = getBufferNode(buffer_id);
if (!bufferNode) continue;
if (!procNode)
procNode = setupProcessNode(pid);
bufferNode->setMapNode(fd, procNode);
procNode->setMapNode(fd, bufferNode);
}
}
}
closedir(ProcessFileDirectory);
}
closedir(proc);
return true;
}
ProcessNode *MapTable::setupProcessNode(unsigned int pid)
{
ProcessNode *processNode;
processNode = getProcessNode(pid);
if (!processNode) {
pair<ProcessType::iterator, bool> node;
node = processList.emplace(pid, ProcessNode(pid));
processNode = &node.first->second;
}
return processNode;
}
void MapTable::printBuffer(BufferNode &bufferNode)
{
// size flags share heap type filecnt iommu_mapped…
// 2097152 0x8 299050 ion_system_heap system 2 12c00000@dpu
//
// PID COMM COUNT
//TRACKING PROCESSES:
// 4078 com.xxx.yyy 1
// 4012 cameraserver 1
// SHARING PROCESSES:
// 4078 com.xxx.yyy 103
// 3987 surfaceflinger 89
// 4012 cameraserver 78
cout << setw(5) << "id" << setw(10) << "size" << setw(8) << "flags";
cout << setw(20) << "heap" << setw(10) << "heaptype" << setw(8) << "filecnt";
cout << setw(3) << " attacheddevice" << endl;
cout << setw(5) << bufferNode.id << setw(10) << bufferNode.size;
cout << setw(8) << bufferNode.getIonFlag();
cout << setw(20) << bufferNode.HeapName << setw(10) << bufferNode.HeapType;
cout << setw(8) << bufferNode.fileCount << setw(3) << " ";
for (auto attached : bufferNode.attachedDevice)
cout << attached.c_str() << " ";
cout << endl;
cout << setw(20) << "SHARING PROCESS : " << setw(8) << "PID";
cout << setw(20) << "COMM" << setw(12) << "FD" << endl;
bool is_entry = false;
for (auto node : bufferNode.mapList) {
if (node.second.fd < 0)
continue;
cout << setw(28) << node.second.procNode->pid;
cout << setw(20) << node.second.procNode->comm;
cout << setw(12) << node.second.fd << endl;
is_entry = true;
}
if (!is_entry)
cout << setw(28) << "No entry" << endl;
cout << setw(20) << "TRACKING PROCESS : " << setw(8) << "PID";
cout << setw(20) << "COMM" << setw(12) << "TRACKCOUNT" << endl;
is_entry = false;
for (auto node : bufferNode.mapList) {
if (node.second.refcount < 0)
continue;
cout << setw(28) << node.second.procNode->pid;
cout << setw(20) << node.second.procNode->comm;
cout << setw(12) << node.second.refcount << endl;
is_entry = true;
}
if (!is_entry)
cout << setw(28) << "No entry" << endl;
cout << endl << endl;
}
void MapTable::printBuffer(unsigned int buffer_id)
{
BufferNode *bufferNode = getBufferNode(buffer_id);
if (!bufferNode) {
cout << "Buffer does not exist for buffer id " << buffer_id << endl;
return;
}
printBuffer(*bufferNode);
}
void MapTable::printProcess(ProcessNode &processNode)
{
//Buffer list of ‘com.xxx.yyy’
// name size flags share heap trkcnt filecnt iommu_mapped…
// ion-47 1048576 cached|needsync 524288 ion_system_heap 1 2 16300000@g2d
// ion-307 2097152 hwrender 699050 ion_system_heap 1 3 12c00000@dpu
cout << "Buffer list of " << processNode.comm << " (" << processNode.pid << ")" << endl;
cout << setw(5) << "id" << setw(5) << "fd" << setw(10) << "size" << setw(8) << "flags";
cout << setw(20) << "heap" << setw(10) << "heaptype" << setw(8) << "trkcnt";
cout << setw(8) << "filecnt" << setw(3) << " attacheddevice" << endl;
for (auto node : processNode.mapList) {
BufferNode *bufferNode = node.second.bufferNode;
cout << setw(5) << bufferNode->id << setw(5) << node.second.fd;
cout << setw(10) << bufferNode->size << setw(8) << bufferNode->getIonFlag();
cout << setw(20) << bufferNode->HeapName << setw(10) << bufferNode->HeapType;
cout << setw(8) << node.second.refcount;
cout << setw(8) << bufferNode->fileCount << setw(3) << " ";
for (auto attached : bufferNode->attachedDevice)
cout << attached.c_str() << " ";
cout << endl;
}
cout << endl << endl;
}
void MapTable::printProcess(unsigned int pid)
{
ProcessNode *procNode = getProcessNode(pid);
if (!procNode) {
cout << "Process does not exist for process id " << pid << endl;
return;
}
printProcess(*procNode);
}
void MapTable::print(void)
{
for (auto buffer : bufferList)
printBuffer(buffer.second);
for (auto process : processList)
printProcess(process.second);
}
void MapTable::printSummary(void)
{
cout << endl;
cout << "Total ion memory : " << totalIonMemory << "KB" << endl << endl;
cout << setw(20) << "Ion memory by process:" << endl;
cout << setw(10) << "PID" << setw(16) << "Resident Size" << setw(21) << "Proportional Size" << " Process" << endl;
// Total Pss by process:
// PID RSS PSS Process
// 0 62472K 62472K Kernel
// 2526 104256K 78192K composer@2.2-se
for (auto process : processList) {
ProcessNode procNode = process.second;
unsigned int pss = 0;
unsigned int rss = 0;
for (auto node : procNode.mapList) {
BufferNode *bufferNode = node.second.bufferNode;
pss += bufferNode->size / bufferNode->mapList.size();
rss += bufferNode->size;
}
cout << setw(10) << procNode.pid << setw(15) << rss / 1024 << "K" << setw(20) << pss / 1024;
cout << "K " << procNode.comm << endl;
}
}
static void printEvent(void)
{
string line;
ifstream event(ION_EVENT_PATH);
if (!event) {
cout << "Event path does not exist (" << ION_EVENT_PATH << ")" << endl;
return;
}
while (getline(event, line))
cout << line << endl;
}
static vector<string> HeapList;
static void printHeap(string heapName)
{
ifstream heap(ION_HEAPS_PATH + heapName);
if (!heap) {
cout << "Heap path does not exist (" << ION_HEAPS_PATH + heapName << ")" << endl;
return;
}
string line;
while (getline(heap, line))
cout << line << endl;
cout << endl;
}
static void printHelp(void)
{
cout << "usage : ionps [-aesh] [-b BUFFER ID] [-p PROCESS ID] [-H HEAP NAME]" << endl;
cout << setw(5) << "-b" << setw(10) << "--buffer" << setw(20) << "<buffer index>";
cout << " show process information that owns request buffer" << endl;
cout << setw(5) << "-p" << setw(10) << "--process" << setw(20) << "<process id>";
cout << " show buffer information that requested process owns" << endl;
cout << setw(5) << "-H" << setw(10) << "--heap" << setw(20) << "<heap name>";
cout << " show /d/ion/heaps/<heap_name>" << endl;
cout << setw(5) << "-e" << setw(10) << "--event" << setw(20) << " ";
cout << " show /d/ion/event" <<endl;
cout << setw(5) << "-a" << setw(10) << "--all" << setw(20) << " ";
cout << " show every buffer, process in detail" << endl;
cout << setw(5) << "-s" << setw(10) << "--summary" << setw(20) << " ";
cout << " show summary such as ion total memory, and rss, pss by process" << endl;
cout << setw(5) << "-h" << setw(10) << "--help" << setw(20) << " ";
cout << " This help message" << endl << endl;
cout << "Available heap name: ";
for (unsigned int i = 0; i < HeapList.size(); i++) {
if (!(i & 3))
cout << endl << setw(10) << " ";
cout << HeapList[i] << ", ";
}
cout << "all (show every heap)" << endl;
}
static void setupHeapName(void)
{
DIR *proc = opendir(ION_HEAPS_PATH);
if (!proc)
return;
struct dirent *HeapDirectory;
while ((HeapDirectory = readdir(proc)) != NULL) {
string directory = HeapDirectory->d_name;
if (!directory.compare(0, 1, "."))
continue;
if (!directory.compare(directory.size() - 6, 6, "shrink"))
continue;
HeapList.push_back(HeapDirectory->d_name);
}
closedir(proc);
}
static bool checkHeapName(string option)
{
for (auto heap : HeapList) {
if (heap == option)
return true;
}
return false;
}
static int args_to_num(const char *optarg)
{
int num;
char *end;
num = strtoul(optarg, &end, 10);
if (optarg == end) {
cout << "Unknown input number" << endl;
return -1;
}
return num;
}
int main(int argc, char *argv[])
{
setupHeapName();
if (argc == 1) {
printHelp();
return 0;
}
MapTable maptable;
if (!(maptable.setupBuffer() && maptable.setupTraceInfo() && maptable.setupMapInfo()))
return -1;
static const struct option long_options[] = {
{"buffer", required_argument, 0, 'b'},
{"process", required_argument, 0, 'p'},
{"heap", required_argument, 0, 'H'},
{"event", required_argument, 0, 'e'},
{"all", no_argument, 0, 'a'},
{"summary", no_argument, 0, 's'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int c, option_index = 0;
if ((c = getopt_long(argc, argv, "b:p:H:eash", long_options, &option_index)) == -1) {
cout << "No argument" << endl;
return -1;
}
switch (c) {
case 'a':
maptable.print();
break;
case 'b':
maptable.printBuffer(args_to_num(optarg));
break;
case 'p':
maptable.printProcess(args_to_num(optarg));
break;
case 'H':
if (!HeapList.size())
cout << "No support about option -H, --heap" << endl;
if (!strncmp(optarg, "all", 3)) {
for (unsigned int i = 0; i < HeapList.size(); i++)
printHeap(HeapList[i]);
} else if (!checkHeapName(optarg)) {
cout << "Unknown heap name " << optarg << endl;
return -1;
} else {
printHeap(optarg);
}
break;
case 'e':
printEvent();
break;
case 'h':
printHelp();
break;
case 's':
maptable.printSummary();
break;
case '?':
cout << "Unknown option " << optopt << " @ " << optind << endl;
return -1;
default:
cout << "Error found during parsing command line options" << endl;
return -1;
}
return 0;
}