Skip to content

Commit

Permalink
Simplify ReaderArray to make it faster; stop pretending files in /pro…
Browse files Browse the repository at this point in the history
…c are normal

Simplify the logic in ReaderArray - don't try to deal with files of
indefinite length in ReaderArray,  or in Readers at all, and just deal
with it where required (specifically, when reading auxv from /proc)
  • Loading branch information
peadar committed May 8, 2024
1 parent a8b4bf0 commit 32e69c6
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 131 deletions.
97 changes: 39 additions & 58 deletions libpstack/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ Reader::readObj(Off offset) const
class FileReader : public Reader {
std::string name;
int file;
protected:
mutable Off fileSize;
public:
virtual size_t read(Off off, size_t count, char *ptr) const override ;
FileReader(std::string name_);
FileReader(std::string name_, Off minsize);
~FileReader();
void describe(std::ostream &os) const override { os << name; }
std::string filename() const override { return name; }
Expand Down Expand Up @@ -206,7 +206,6 @@ class OffsetReader final : public Reader {
};

Reader::csptr loadFile(const std::string &path);
Reader::csptr loadFile(const std::string &path, Reader::Off minsize);

// This allows a reader to provide an iterator over a sequence of objects of a
// given type. Given a reader r, we can use
Expand All @@ -216,82 +215,64 @@ Reader::csptr loadFile(const std::string &path, Reader::Off minsize);
template <typename T, size_t cachesize = 1024 / sizeof(T) >
class ReaderArray {
const Reader &reader;
mutable std::array<T, cachesize> data;
mutable Reader::Off dataOff = 0; // offset of data[0]
mutable size_t dataCount = 0; // number of valid items in data.
size_t initialOffset;
mutable Reader::Off eof; // dynamically discovered EOF marker.
inline size_t fillcache(Reader::Off) const;
inline const T &getitem(Reader::Off) const;
size_t base; // All offsets are relative to this in the underlying reader, and are scaled by size

mutable size_t cacheStart = 0; // index of cache[0] in the array from offset base in the reader.
mutable size_t cacheEnd = 0; // after last valid item in cache.
mutable size_t eof;
mutable std::array<T, cachesize> cache;

public:
class iterator {
const ReaderArray<T, cachesize> *array;
Reader::Off offset; // offset of the iterator itself.
const ReaderArray<T, cachesize> &array;
size_t idx; // Index of current item
public:
const T &operator *();
iterator(const ReaderArray<T, cachesize> *array_) : array(array_), offset(array->reader.size()) {
// this is the EOF iterator - no cache.
const T &operator *() const {
return array.getitem( idx );
}
iterator(const ReaderArray<T, cachesize> *array_, Reader::Off offset_) : array(array_), offset(offset_) {
array->fillcache(offset);
}
bool operator == (const iterator &rhs) const {
return offset == rhs.offset || ( offset >= array->eof && rhs.offset >= rhs.array->eof );
iterator(const ReaderArray<T, cachesize> &array_, size_t idx_) noexcept : array(array_), idx(idx_) { }
iterator(const ReaderArray<T, cachesize> &array_) noexcept : array(array_), idx(array.eof) { }
bool operator == (const iterator &rhs) const noexcept {
return idx == rhs.idx || ( idx >= array.eof && rhs.idx >= rhs.array.eof );
}
bool operator != (const iterator &rhs) const noexcept { return ! (*this == rhs); }
size_t operator - (const iterator &rhs) const noexcept { return (offset - rhs.offset) / sizeof(T); }
iterator & operator++();
size_t operator - (const iterator &rhs) const noexcept { return idx - rhs.idx; }
iterator & operator++() noexcept;
};

using const_iterator = iterator;
typedef T value_type;
iterator begin() const { return iterator(this, initialOffset); }
iterator end() const { return iterator(this); }

ReaderArray(const Reader &reader_, size_t offset = 0) : reader(reader_), initialOffset(offset), eof(reader.size()) {
assert(reader.size() == std::numeric_limits<Reader::Off>::max() || reader.size() % sizeof (T) == 0);
iterator begin() const { return iterator(*this, 0); }
iterator end() const { return iterator(*this); }
const inline T &getitem(size_t) const;

ReaderArray(const Reader &reader_, size_t offset = 0) :
reader(reader_),
base(offset),
eof( ( reader.size() - base ) / sizeof(T) ) {
assert(reader.size() == std::numeric_limits<size_t>::max() || reader.size() % sizeof (T) == 0);
}
};

template<typename T, size_t cachesize>
typename ReaderArray<T, cachesize>::iterator &ReaderArray<T, cachesize>::iterator::operator ++() {
offset += sizeof (T);
array->fillcache(offset);
typename ReaderArray<T, cachesize>::iterator &ReaderArray<T, cachesize>::iterator::operator ++() noexcept {
++idx;
return *this;
}

template <typename T, size_t cachesize> const T &ReaderArray<T, cachesize>::iterator::operator *() {
return array->getitem(offset);
}
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

template <typename T, size_t cachesize> size_t ReaderArray<T, cachesize>::fillcache(Reader::Off offset) const {
if (offset >= eof)
return 0;
size_t idx = (offset - dataOff) / sizeof (T);
if (dataOff > offset || (offset - dataOff) / sizeof (T) >= dataCount) {
idx = 0;
dataOff = offset;
auto rc = reader.read(offset, cachesize * sizeof (T), (char *)&data[0]);
dataCount = rc / sizeof(T);
if (dataCount == 0) { // short read - consider this EOF.
eof = offset;
return 0;
template <typename T, size_t cachesize> const T &ReaderArray<T, cachesize>::getitem(size_t idx) const {
if (unlikely(cacheStart > idx || idx >= cacheEnd)) {
size_t rc = reader.read(idx * sizeof(T) + base, cachesize * sizeof (T), reinterpret_cast<char *>(cache.data()));
cacheStart = idx;
cacheEnd = cacheStart + rc / sizeof(T);
if (unlikely(rc < sizeof(T))) { // short read - consider this EOF.
throw ( Exception() << "end of data while reading array" );
}
}
return idx;
}

template <typename T, size_t cachesize> const T &ReaderArray<T, cachesize>::getitem(Reader::Off offset) const {
// If the item is not already in the cache, fill the cache as much as we can starting with this item
size_t idx = fillcache(offset);

if (idx >= dataCount)
throw ( Exception() << "end of data while reading array" );
return data[idx];
}


return cache[idx - cacheStart];
}

template <typename T, typename Iter> static inline std::pair<T, size_t> readleb128(Iter start) {
Expand All @@ -313,5 +294,5 @@ template <typename T, typename Iter> static inline std::pair<T, size_t> readleb1
}
};
}

}
#endif
5 changes: 3 additions & 2 deletions live.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ procname(pid_t pid, const std::string &base)
return linkResolve(stringify("/proc/", pid, "/", base));
}

LiveReader::LiveReader(pid_t pid, const std::string &base)
: FileReader(procname(pid, base), std::numeric_limits<Reader::Off>::max()) {}
LiveReader::LiveReader(pid_t pid, const std::string &base) : FileReader(procname(pid, base)) {
fileSize = std::numeric_limits<Reader::Off>::max();
}

LiveProcess::LiveProcess(Elf::Object::sptr &ex, pid_t pid_,
const PstackOptions &options, Dwarf::ImageCache &imageCache, bool alreadyStopped)
Expand Down
119 changes: 62 additions & 57 deletions process.cc
Original file line number Diff line number Diff line change
Expand Up @@ -221,72 +221,77 @@ auxtype2str(int auxtype) {
void
Process::processAUXV(const Reader &auxio)
{
try {
for (auto &aux : ReaderArray<Elf::auxv_t>(auxio)) {
Elf::Addr hdr = aux.a_un.a_val;
switch (aux.a_type) {
case AT_ENTRY: {
if (verbose > 2)
*debug << "auxv: AT_ENTRY=" << hdr << std::endl;
// this provides a reference for relocating the executable when
// compared to the entrypoint there.
entry = hdr;
break;
}
case AT_SYSINFO: {
if (verbose > 2)
*debug << "auxv:AT_SYSINFO=" << hdr << std::endl;
sysent = hdr;
break;
}
case AT_SYSINFO_EHDR: {
try {
auto elf = std::make_shared<Elf::Object>(imageCache, io->view("(vdso image)", hdr, 65536));
vdsoBase = hdr;
addElfObject("(vdso image)", elf, hdr);
vdsoImage = elf;
if (verbose >= 2) {
*debug << "auxv: VDSO " << *elf->io
<< " loaded at " << std::hex << hdr << std::dec << "\n";
}

for (auto &aux : ReaderArray<Elf::auxv_t>(auxio)) {
Elf::Addr hdr = aux.a_un.a_val;
switch (aux.a_type) {
case AT_ENTRY: {
if (verbose > 2)
*debug << "auxv: AT_ENTRY=" << hdr << std::endl;
// this provides a reference for relocating the executable when
// compared to the entrypoint there.
entry = hdr;
break;
}
case AT_SYSINFO: {
if (verbose > 2)
*debug << "auxv:AT_SYSINFO=" << hdr << std::endl;
sysent = hdr;
break;
}
case AT_SYSINFO_EHDR: {
try {
auto elf = std::make_shared<Elf::Object>(imageCache, io->view("(vdso image)", hdr, 65536));
vdsoBase = hdr;
addElfObject("(vdso image)", elf, hdr);
vdsoImage = elf;
if (verbose >= 2) {
*debug << "auxv: VDSO " << *elf->io
<< " loaded at " << std::hex << hdr << std::dec << "\n";
}

}
catch (const std::exception &ex) {
std::clog << "auxv: warning: failed to load DSO: " << ex.what() << "\n";
catch (const std::exception &ex) {
std::clog << "auxv: warning: failed to load DSO: " << ex.what() << "\n";
}
break;
}
break;
}
case AT_BASE:
if (verbose > 2)
*debug << "auxv: AT_BASE=" << hdr << std::endl;
interpBase = hdr;
break;
case AT_BASE:
if (verbose > 2)
*debug << "auxv: AT_BASE=" << hdr << std::endl;
interpBase = hdr;
break;
#ifdef AT_EXECFN
case AT_EXECFN: {
if (verbose > 2)
*debug << "auxv: AT_EXECFN=" << hdr << std::endl;
try {
auto exeName = io->readString(hdr);
if (verbose >= 2)
*debug << "filename from auxv: " << exeName << "\n";
if (!execImage) {
execImage = imageCache.getImageForName(exeName);
if (entry == 0)
entry = execImage->getHeader().e_entry;
case AT_EXECFN: {
if (verbose > 2)
*debug << "auxv: AT_EXECFN=" << hdr << std::endl;
try {
auto exeName = io->readString(hdr);
if (verbose >= 2)
*debug << "filename from auxv: " << exeName << "\n";
if (!execImage) {
execImage = imageCache.getImageForName(exeName);
if (entry == 0)
entry = execImage->getHeader().e_entry;
}
}
catch (const Exception &ex) {
*debug << "failed to read AT_EXECFN: " << ex.what() << std::endl;
}
}
catch (const Exception &ex) {
*debug << "failed to read AT_EXECFN: " << ex.what() << std::endl;
}

break;
}
break;
}
#endif
default:
if (verbose > 2)
*debug << "auxv: " << auxtype2str( aux.a_type) << ": " << hdr << std::endl;
default:
if (verbose > 2)
*debug << "auxv: " << auxtype2str( aux.a_type) << ": " << hdr << std::endl;
}
}
} catch (const std::exception &ex) {
if (verbose)
std::clog << "exception while reading auxv: " << ex.what() << "\n";
}

}

static bool
Expand Down
14 changes: 0 additions & 14 deletions reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ FileReader::FileReader(string name_)
fileSize = buf.st_size;
}

FileReader::FileReader(string name_, Off minsize)
: FileReader(name_)
{
fileSize = std::max(fileSize, minsize);
}

FileReader::~FileReader()
{
::close(file);
Expand Down Expand Up @@ -223,14 +217,6 @@ loadFile(const string &path)
std::make_shared<FileReader>(path));
}

Reader::csptr
loadFile(const string &path, Reader::Off minsize)
{
return std::make_shared<CacheReader>(
std::make_shared<FileReader>(path, minsize));
}


MmapReader::MmapReader(const string &name_)
: MemReader(name_, 0, nullptr)
{
Expand Down

0 comments on commit 32e69c6

Please sign in to comment.