Add MemoryMap to read /proc/[pid]/maps

This commit is contained in:
Théophile Bastian 2018-04-03 12:23:15 +02:00
parent ea7405e3ba
commit 13283d8e2a
2 changed files with 198 additions and 0 deletions

102
src/MemoryMap.cpp Normal file
View file

@ -0,0 +1,102 @@
#include "MemoryMap.hpp"
#include <unistd.h>
#include <fstream>
#include <sstream>
using namespace std;
void MemoryMap::MapEntry::dump(ostream& os) const {
os << "MapEntry: "
<< hex << mem_region.begin << '-' << mem_region.end
<< " offset " << offset
<< dec
<< " - <" << pathname << ">"
<< endl;
}
MemoryMap::MemoryMap() {
int pid = getpid();
ostringstream path;
path << "/proc/" << pid << "/maps";
ifstream handle(path.str());
if(handle.fail())
throw ReadMapFailed();
string line;
while(getline(handle, line).good()) {
istringstream lstream(line);
MapEntry entry;
uintptr_t discard;
lstream >> std::hex;
lstream >> entry.mem_region.begin;
lstream.ignore(1);
lstream >> entry.mem_region.end;
lstream.ignore(6);
lstream >> discard;
lstream.ignore(7);
lstream >> entry.offset;
lstream >> entry.pathname;
rev_addr_map.insert(make_pair(entry.mem_region, map_entries.size()));
map_entries.push_back(entry);
}
handle.close();
}
const MemoryMap::MapEntry& MemoryMap::get_entry(size_t id) const {
try {
return map_entries.at(id);
} catch(const std::out_of_range& e) {
// Just here to remind one to throw a std::out_of_range if the
// underlying structure is changed at some point, and doesn't raise
// std::out_of_range anymore upon .at()
throw e;
}
}
const MemoryMap::MapEntry& MemoryMap::operator[](size_t id) const {
return get_entry(id);
}
size_t MemoryMap::id_of_address(uintptr_t addr) const {
/* I have found no nice operator< for MemoryRegions that work nice with
* both the internal consistency of map_entries and the search of the
* containing interval.
* We thus have two cases:
* - either we seek the range [a, b[ containing a,
* - or the range [a, b[ containing some c, a <= c < b.
*
* In the first case, if such a range exists, it is necessarily
* lower_bound(MemoryRegion(a, a)).
* In the second case, it is necessarily
* --lower_bound(MemoryRegion(c, c)).
*/
MemoryRegion reg_index(addr, addr);
auto bound_it = rev_addr_map.lower_bound(reg_index);
if(bound_it == rev_addr_map.end())
throw std::out_of_range("No region containing this address");
if(bound_it->first.contains(addr))
return bound_it->second;
if(bound_it != rev_addr_map.begin()) {
--bound_it;
if(bound_it->first.contains(addr))
return bound_it->second;
}
throw std::out_of_range("No region containing this address");
}
MemoryMap::iter_t MemoryMap::begin() const {
return map_entries.cbegin();
}
MemoryMap::iter_t MemoryMap::end() const {
return map_entries.cend();
}
size_t MemoryMap::size() const {
return map_entries.size();
}

96
src/MemoryMap.hpp Normal file
View file

@ -0,0 +1,96 @@
#pragma once
/** MemoryMap: load, parse and make available the process' memory map
*
* This class allows the easy loading, parsing and reading of the process'
* memory map, that is, the correspondance between the various compile units
* and shared libraries' sections, and their mapping location in memory.
*
* This class is absolutely not portable and reads /proc/[pid]/maps. For more
* information, please read `man 5 proc`.
*/
#include <exception>
#include <stdexcept>
#include <map>
#include <vector>
#include <string>
#include <cstdint>
class MemoryMap {
public:
/// Thrown when the constructor fails to read the map's data
class ReadMapFailed: public std::exception {};
struct MemoryRegion {
/** A contiguous memory region. */
MemoryRegion() = default;
MemoryRegion(uintptr_t begin, uintptr_t end)
: begin(begin), end(end) {}
uintptr_t begin; ///< First address (incl.) in the region
uintptr_t end; ///< Past-the-end address for the region
bool operator==(const MemoryRegion& oth) const {
return begin == oth.begin && end == oth.end;
}
bool operator<(const MemoryRegion& oth) const {
if(begin < oth.begin)
return true;
return (begin == oth.begin && end < oth.end);
}
/// Checks whether `addr` is in this region
bool contains(uintptr_t addr) const {
return begin <= addr && addr < end;
}
};
struct MapEntry {
std::string pathname;
MemoryRegion mem_region;
uintptr_t offset;
/// Debug feature: dumps the MapEntry on the given stream
void dump(std::ostream& os) const;
};
/// An iterator to the map entries. The underlying type might change.
typedef std::vector<MapEntry>::const_iterator iter_t;
public:
/** Loads and constructs the map.
*
* \throws ReadMapFailed upon failure */
MemoryMap();
/** Get the MapEntry for a given id
*
* \throws std::out_of_range if there is no such entry */
const MapEntry& get_entry(size_t id) const;
/// Synonymous for get_entry
const MapEntry& operator[](size_t id) const;
/** Get the MapEntry id of the region containing addr
*
* \throws std::out_of_range if no such region is mapped */
size_t id_of_address(uintptr_t addr) const;
/// Get a constant iterator to the first map entry
iter_t begin() const;
/// Get a constant iterator to a past-the-end iterator
iter_t end() const;
/// Get the number of entries in the map
size_t size() const;
private:
std::vector<MapEntry> map_entries;
/// Maps regions back to their id
std::map<MemoryRegion, size_t> rev_addr_map;
};