Skip to content

Instantly share code, notes, and snippets.

@zvrba zvrba/memshared.cpp
Created Apr 18, 2015

Embed
What would you like to do?
Compute the amount of memory saved through code sharing of SOs
// See https://www.kernel.org/doc/Documentation/vm/pagemap.txt
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <stdexcept>
#include <utility>
using std::vector;
using std::string;
using std::cout;
using std::cerr;
using std::endl;
static uint64_t G_pagesize;
static vector<uint16_t> G_refcounts;
static int G_kpagecountfd;
template<typename F, typename... Args>
static long syscall(const char *what, F f, Args... args)
{
long result = f(args...);
if (result < 0) {
std::ostringstream err;
err << what << ": " << strerror(errno);
throw std::runtime_error(err.str());
}
return result;
}
class PageMap
{
std::ifstream _ifs;
int _pmapfd;
bool _verbose;
void ProcessMaps();
void ProcessRange(const string &soname, uint64_t loaddr, uint64_t hiaddr);
uint16_t ProcessPage(uint64_t pfn);
public:
PageMap(int pid);
~PageMap()
{
if (_pmapfd >= 0) close(_pmapfd);
}
void Process(bool verbose);
};
PageMap::PageMap(int pid) : _pmapfd(-1)
{
{
std::ostringstream oss;
oss << "/proc/" << pid << "/maps";
_ifs.open(oss.str().c_str());
if (!_ifs.is_open() && errno != ENOENT)
throw std::runtime_error("can't open maps file");
}
{
std::ostringstream oss;
oss << "/proc/" << pid << "/pagemap";
_pmapfd = syscall("open(pmapfd)", open, oss.str().c_str(), O_RDONLY);
}
}
void PageMap::Process(bool verbose)
{
string line, flags, dev, soname;
uint64_t loaddr, hiaddr, offset, ino;
char delim;
_verbose = verbose;
while (getline(_ifs, line)) {
std::istringstream iss(line);
iss >> std::hex >> loaddr >> delim >> hiaddr >> flags >> offset >> dev >> ino >> soname;
if (!iss)
continue; // Happens when too few fields (soname missing)
if (iss && flags == "r-xp" && ino)
ProcessRange(soname, loaddr, hiaddr);
}
}
void PageMap::ProcessRange(const string &soname, uint64_t loaddr, uint64_t hiaddr)
{
uint64_t pfn_flags;
loaddr /= G_pagesize;
hiaddr /= G_pagesize;
if (_verbose)
cout << soname << endl;
for (uint64_t page = loaddr; page < hiaddr; ++page) {
syscall("lseek(pmapfd)", lseek, _pmapfd, 8*page, SEEK_SET);
syscall("read(pmapfd)", read, _pmapfd, &pfn_flags, 8);
if ((pfn_flags >> 63) == 1) { // present?
uint64_t pfn = pfn_flags & ((1UL << 55)-1);
auto count = ProcessPage(pfn);
if (_verbose)
cout << std::hex << std::setw(12) << pfn << " " << count << endl;
}
}
}
uint16_t PageMap::ProcessPage(uint64_t pfn)
{
if (!G_refcounts[pfn])
{
uint64_t pagecount;
syscall("lseek(kpagecountfd)", lseek, G_kpagecountfd, 8*pfn, SEEK_SET);
syscall("read(kpagecountfd)", read, G_kpagecountfd, &pagecount, 8);
if (pagecount > 65535)
throw std::runtime_error("pagecount too big");
G_refcounts[pfn] = pagecount;
}
return G_refcounts[pfn];
}
static vector<int> CollectPids()
{
vector<int> pids;
DIR *proc = opendir("/proc");
struct dirent *de;
int pid;
while ((de = readdir(proc)) != 0) {
std::istringstream iss(de->d_name);
if (iss >> pid)
pids.push_back(pid);
}
closedir(proc);
return pids;
}
static void Report()
{
uint64_t shared = 0;
for (auto x : G_refcounts) {
if (x > 0)
shared += x-1;
}
cout << "Total SAVED by sharing: " << shared << endl;
}
int main(int argc, char **argv) try
{
if ((G_pagesize = sysconf(_SC_PAGESIZE)) < 0)
throw std::runtime_error(strerror(errno));
if ((G_kpagecountfd = open("/proc/kpagecount", O_RDONLY)) < 0)
throw std::runtime_error(strerror(errno));
G_refcounts.resize(1 << 21, 0); // covers up to 8G physical pages
if (argc == 2) {
PageMap(atoi(argv[1])).Process(true);
} else {
auto pids = CollectPids();
for (int pid : pids)
PageMap(pid).Process(false);
}
Report();
return 0;
}
catch (std::runtime_error &err)
{
cerr << "ERROR: " << err.what() << endl;
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.