/*
*
* RLDebugger - Resource Leakage Debugger
* Autor: Tomasz Jaworski, 2018-2019
*
* ==================================================
* Plik nagłówkowy rdebug.h _MUSI_ być dodany na końcu listy #include w kompilowanym pliku .C
* Przykład:
* #include <stdio.h>
* #include <string.h>
* #include "rdebug.h"
*
* ...
* int main(....)
*/
#if !defined(_RESOURCE_DEBUG_H_)
#define _RESOURCE_DEBUG_H_
enum heap_function_code_t {
__hfc_min = 0,
HFC_MALLOC = 1,
HFC_FREE,
HFC_CALLOC,
HFC_REALLOC,
HFC_STRDUP,
HFC_STRNDUP,
HFC_EXIT,
HFC_FOPEN, // wip
HFC_FCLOSE, // wip
__hfc_max
};
#if !defined(_RLDEBUG_IMPLEMENTATION_)
#if defined(__malloc_and_calloc_defined) || defined(_MALLOC_H)
void* _rldebug_heap_wrapper(
enum heap_function_code_t call_type, // Typ wywołania - identyfikator funkcji w imieniu której wywoływany jest wrapper
void* ptr, // ARGUMENT 0: Wskaźnik przekazany do fukncji
size_t number, // ARGUMENT 1: pierwsza liczba przekazana do funkcji (zaraz za wskaźnikiem)
size_t size, // ARGUMENT 2: druga liczba przekazana do funkcji (tylko calloc)
const char* source_name, // Nazwa pliku, w którym pojawiło się dane wywołanie
int source_line // Linia w pliku, z której pojawiło się wywołanie
);
// przejęcie funkcji obsługi sterty dla biblioteki standardowej C
#define malloc(n) _rldebug_heap_wrapper(HFC_MALLOC, NULL, n, 0, __FILE__, __LINE__)
#define free(p) _rldebug_heap_wrapper(HFC_FREE, p, 0, 0, __FILE__, __LINE__)
#define calloc(n, s) _rldebug_heap_wrapper(HFC_CALLOC, NULL, n, s, __FILE__, __LINE__)
#define realloc(p, n) _rldebug_heap_wrapper(HFC_REALLOC, p, n, 0, __FILE__, __LINE__)
#endif // __malloc_and_calloc_defined
#if defined(_STRING_H)
char* _rldebug_str_wrapper(
enum heap_function_code_t call_type, // Typ wywołania - identyfikator funkcji w imieniu której wywoływany jest wrapper
const char* pstring, // ARGUMENT 0: Wskaźnik przekazany do fukncji
size_t number, // ARGUMENT 1: pierwsza liczba przekazana do funkcji (zaraz za wskaźnikiem)
const char* source_name, // Nazwa pliku, w którym pojawiło się dane wywołanie
int source_line // Linia w pliku, z której pojawiło się wywołanie
);
// kolizja z GNU Features
#if defined __USE_XOPEN2K8
#undef strdup
#undef strndup
#endif
#define strdup(p) _rldebug_str_wrapper(HFC_STRDUP, p, 0, __FILE__, __LINE__)
#define strndup(p, n) _rldebug_str_wrapper(HFC_STRNDUP, p, n, __FILE__, __LINE__)
#endif // _STRING_H
#if defined(_STDIO_H)
void* _rldebug_io_wrapper(enum heap_function_code_t call_type, FILE* stream, const char* stream_name,
const char* stream_mode, const char* source_file, int source_line);
#define fopen(f, m) ((FILE*)_rldebug_io_wrapper (HFC_FOPEN, NULL, f, m, __FILE__, __LINE__))
#if __WORDSIZE == 64
// x64
#define fclose(stream) ((unsigned long int) \
_rldebug_io_wrapper (HFC_FCLOSE, stream, NULL, NULL, __FILE__, __LINE__))
#else // if __WORDSIZE == 64
// x32
#define fclose(stream) ((int)_rldebug_io_wrapper (HFC_FCLOSE, stream, NULL, NULL, __FILE__, __LINE__))
#endif // if __WORDSIZE == 64
#endif // _STDIO_H
#if defined(_STDLIB_H)
void* __attribute__((noreturn)) _rldebug_stdlib_noreturn_wrapper(enum heap_function_code_t call_type, int iarg, const char* source_file, int source_line);
#define exit(__status) _rldebug_stdlib_noreturn_wrapper(HFC_EXIT, __status, __FILE__, __LINE__)
#endif // _STDLIB_H
// #if defined(__malloc_and_calloc_defined) || defined(_STRING_H)
// #endif // defined(__malloc_and_calloc_defined) || defined(_STRING_H)
#endif // _RLDEBUG_IMPLEMENTATION_
#define SIGHEAP SIGUSR2 // redefinicja sygnału USR2 na potrzeby debuggera zasobów
int rdebug_call_main(int (*pmain)(int, char**, char**), int argc, char** argv, char** envp);
//
// #############################################################################
// ##
// ## Interfejs publiczny
// ##
// #############################################################################
#if defined _RLDEBUG_API_
#define RLD_HEAP_UNLIMITED (SIZE_MAX >> 1)
#define RLD_UNKNOWN_POINTER (SIZE_MAX >> 1)
enum message_severity_level_t {
MSL_QUIET = 0,
MSL_INFORMATION,
MSL_WARNING,
MSL_FAILURE
};
int rldebug_show_leaked_resources(int force_empty_summary);
//
// ## ustwienia, ograniczniki, płotki, etc..
//
// Uchyl wszystkie ograniczniki sterty i zasobów
void rldebug_reset_limits(void);
// Ustaw poziom szczegółowości raportowania akcji testowanego programu
void rldebug_set_reported_severity_level(enum message_severity_level_t lowest_level);
// Ustaw globalny limit sterty
void rldebug_heap_set_global_limit(size_t heap_limit /* = RLD_HEAP_UNLIMITED */);
// Ustal jednostkowy limit alokacji pamięci (nie więcej niz `limit` bajtów na jedno wywołanie funkcji `call_type`
void rldebug_heap_set_function_singleshot_limit(enum heap_function_code_t call_type, size_t limit);
// Włacz/wyłacz wszystkie funkcje obsługi sterty
void rldebug_heap_disable_all_functions(int disabled);
// Wyznacz wielkość wycieku
size_t rldebug_heap_get_leak_size(void);
// Funkcja rldebug_get_block_size(ptr) zwraca wielkość bloku reprezentowanego przez wskaźnik ptr albo RLD_UNKNOWN_POINTER jeśli wskaźnik nie reprezentuje bloku
// Reprezentacja bloku przez wskaźnik - wskaźnik wskazuje na pierwszy bajt bloku a nie w dowolne jego miejsce
size_t rldebug_get_block_size(const void* ptr);
// Funkcja ustawia skumulowany limit alokacji pamięci dla funkcji call_type.
void rldebug_heap_set_function_cumulative_limit(enum heap_function_code_t call_type, size_t limit);
// Funkcja ustawia liczbę wywołań danej funkcji call_type, które zakończą się sukcesem
void rldebug_set_function_success_limit(enum heap_function_code_t call_type, size_t limit);
//
// Podłączenie do testów jednostkowych
//
// Raportuj BŁĄD i PRZERWIJ dany test jeśli wykryto wycieki pamięci
#define test_no_heap_leakage() do { \
if (rldebug_heap_get_leak_size() > 0) \
test_result_internal(TEST_FAILED, __LINE__, "Wykryto wyciek pamięci."); \
else \
test_result_internal(TEST_NONE, __LINE__, ""); \
} while(0)
#endif // _RLDEBUG_API_
#endif // _RESOURCE_DEBUG_H_