Add MemoryMap to read /proc/[pid]/maps
This commit is contained in:
parent
ea7405e3ba
commit
13283d8e2a
2 changed files with 198 additions and 0 deletions
102
src/MemoryMap.cpp
Normal file
102
src/MemoryMap.cpp
Normal 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
96
src/MemoryMap.hpp
Normal 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;
|
||||||
|
};
|
Loading…
Reference in a new issue