Skip to content

Instantly share code, notes, and snippets.

@terrelln
Last active April 11, 2016 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save terrelln/b19a545e51a8b53d8fedfd0ee12e3b91 to your computer and use it in GitHub Desktop.
Save terrelln/b19a545e51a8b53d8fedfd0ee12e3b91 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <cstdlib>
#include <cstddef>
#include <iostream>
namespace monstrous {
struct DataNode;
/**
* The global variable for the linked list.
*/
DataNode *data_node = nullptr;
/**
* Node to store information about allocations.
* We will construct a linked list of these nodes in static storage with some
* macro fun.
*/
struct DataNode {
const char *file;
const char *func;
unsigned long line;
std::size_t allocations = 0;
std::size_t bytes = 0;
std::size_t failed_allocations = 0;
std::size_t failed_bytes = 0;
DataNode *next;
/**
* Add itself to the static linked list upon construction.
*/
explicit DataNode(const char *file, const char *func, unsigned long line)
: file(file), func(func), line(line), next(data_node) {
data_node = this;
}
};
/**
* Print out all the data in the linked list.
*/
void print() {
while (data_node != nullptr) {
std::cout << data_node->file << ':' << data_node->func << ':'
<< data_node->line << ':' << " was called "
<< data_node->allocations << " times, and allocated "
<< data_node->bytes << " bytes." << std::endl;
data_node = data_node->next;
}
}
/**
* We pass the necessary data from the lambda to operator+ in this struct.
*/
struct horrid {
DataNode *node;
std::size_t n;
};
/**
* There is both `malloc` and `std::malloc`, and we don't know which will be
* called, but we want our macro to work for both.
* We will pass the malloc function in as the first parameter to operator+, and
* our data passing struct in as the second.
*/
void *operator+(void *(the_malloc)(std::size_t),
::monstrous::horrid h) noexcept {
void *ptr = the_malloc(h.n);
if (ptr == nullptr) {
++h.node->failed_allocations;
h.node->failed_bytes += h.n;
}
return ptr;
}
}
/**
* Whenever the user calls `malloc` or `std::malloc`, expand it to this
* statement. The lambda handles the parameters to malloc by stealing them,
* allocates a static DataNode, populates it with information, and sends the
* data passing struct to operator+.
*/
#define malloc \
malloc + [](::std::size_t n) -> ::monstrous::horrid { \
static ::monstrous::DataNode node{__FILE__, __func__, __LINE__}; \
++node.allocations; \
node.bytes += n; \
return {&node, n}; \
}
int main() {
// Yay! it works for both `std::malloc` and `malloc`!
void *x = std::malloc(5);
void *y = malloc(6);
// Print the results, could put this in SCOPE_EXIT, or a signal handler.
// Even works with `bad_alloc`, because no dynamic memory is required!
::monstrous::print();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment