Модульное тестирование для неудачного malloc()

Каков наилучший способ для модульного тестирования путей кода с неудачным malloc()? В большинстве случаев это, вероятно, не имеет значения, потому что вы делаете что-то вроде

thingy *my_thingy = malloc(sizeof(thingy));
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
} 

но в некоторых случаях у вас есть выбор, кроме смерти, потому что вы выделили дополнительный материал для кэширования или чего-то еще, и вы можете вернуть эту память.

Однако в тех случаях, когда вы можете попытаться восстановиться после сбоя malloc(), вы делаете что-то сложное и подверженное ошибкам в довольно необычном пути кода, что делает тестирование особенно важным. Как вы на самом деле собираетесь это делать?


person Pillsy    schedule 10.11.2009    source источник
comment
Вы можете перехватить malloc() и заставить его иногда возвращать 0.   -  person Carl Norum    schedule 11.11.2009
comment
Многие библиотечные функции, такие как printf, могут дать сбой, если процессу не хватает памяти.   -  person ephemient    schedule 11.11.2009
comment
@ephemient Что в порядке, если fprintf() справится с этим правильно. ;-)   -  person Amin Negm-Awad    schedule 08.01.2018


Ответы (6)


Я увидел крутое решение этой проблемы, которое мне представил С. Пааволайнен. Идея состоит в том, чтобы переопределить стандартный malloc(), который вы можете сделать только в компоновщике, с помощью пользовательского распределителя, который

  1. читает текущий стек выполнения потока, вызывающего malloc()
  2. checks if the stack exists in a database that is stored on hard disk
    1. if the stack does not exist, adds the stack to the database and returns NULL
    2. если стек уже существует, обычно выделяет память и возвращает

Затем вы просто запускаете свой модульный тест много раз: эта система автоматически перебирает различные пути управления до malloc() отказа и намного эффективнее и надежнее, чем, например. случайное тестирование.

person Antti Huima    schedule 10.11.2009
comment
Хороший ответ. Не полагается на случайное обнаружение проблем и позволяет систематически проверять последствия сбоя распределения. - person quark; 13.11.2009
comment
+1 за полный охват случайного тестирования. SQLite делает нечто подобное sqlite.org/malloc.html#testing. Ошибки malloc() инициируются с помощью счетчика вместо проверки стека на уникальность. - person Peter Le Bek; 08.08.2011
comment
Теория на этом твердая, и я хотел бы реализовать это. В вашем ответе на самом деле не было деталей реализации, и поиск S. Paavolainen malloc тоже ничего не дал. Можно ли предоставить несколько деталей реализации? - person Dodzi Dzakuma; 22.05.2014
comment
Я сделал общую библиотеку, которая реализует это поведение. Его можно использовать без изменения исполняемого файла через LD_PRELOAD, если доступны символы отладки. github.com/ralight/mallocfail - person ralight; 09.06.2018

Я предлагаю создать специальную функцию для вашего специального кода malloc, который, как вы ожидаете, может дать сбой, и вы могли бы изящно его обработать. Например:

void* special_malloc(size_t bytes) {
  void* ptr = malloc(bytes);
  if(ptr == NULL) {
    /* Do something crafty */
  } else {
    return ptr;
  }
}

Затем вы можете провести модульное тестирование этого хитрого дела, передав некоторые неверные значения для байтов. Вы можете поместить это в отдельную библиотеку и создать фиктивную библиотеку, которая ведет себя специально для тестирования функций, вызывающих эту.

person Dave    schedule 10.11.2009

Это довольно грубо, но если вам действительно нужно модульное тестирование, вы можете сделать это с помощью #ifdefs:

thingy *my_thingy = malloc(sizeof(thingy));
#ifdef MALLOC_UNIT_TEST_1
my_thingy = NULL;
#endif
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
}

К сожалению, вам придется многое перекомпилировать с этим решением.

Если вы используете Linux, вы также можете рассмотреть возможность запуска кода в условиях нехватки памяти, используя ulimit, но будьте осторожны.

person Seth    schedule 10.11.2009

напишите свою собственную библиотеку, которая реализует malloc путем случайного сбоя или вызова реального malloc (статически связанного или явно dlopened)

затем LD_PRELOAD его

person pm100    schedule 10.11.2009
comment
В итоге я остановился на этом методе. Для использования с моей тестовой средой требуется несколько дополнительных флагов компиляции, но она очень гибкая. Кроме того, в моей библиотеке общих объектов вместо случайного сбоя malloc я объявил глобальное значение, которое вызовет сбой malloc, когда я укажу. Эта переменная должна была быть объявлена ​​как extern в моем тестовом коде. - person Dodzi Dzakuma; 23.05.2014

Во FreeBSD я однажды просто перегрузил модуль malloc.o библиотеки C (символы там были слабыми) и заменил реализацию malloc() на такую, которая контролировала вероятность сбоя. Поэтому я подключился статически и начал выполнять тестирование. srandom() завершил изображение контролируемой псевдослучайной последовательностью.

Также посмотрите здесь набор хороших инструментов, которые, по моему мнению, вам понадобятся. По крайней мере, они перегружают malloc() / free() для отслеживания утечек, так что кажется удобной точкой для добавления всего, что вы хотите.

person Roman Nikitchenko    schedule 10.11.2009

Вы можете захватить malloc, используя некоторые определения и глобальные параметры для управления им... Это немного хакерски, но, похоже, работает.

#include <stdio.h>
#include <stdlib.h>

#define malloc(x) fake_malloc(x)

struct {
  size_t last_request;
  int should_fail;
  void *(*real_malloc)(size_t);
} fake_malloc_params;

void *fake_malloc(size_t size) {
  fake_malloc_params.last_request = size;
  if (fake_malloc_params.should_fail) {
    return NULL;
  }
  return (fake_malloc_params.real_malloc)(size);;
}

int main(void) {
  fake_malloc_params.real_malloc = malloc;
  void *ptr = NULL;
  ptr = malloc(1);
  printf("last: %d\n", (int) fake_malloc_params.last_request);
  printf("ptr: 0x%p\n", ptr);
  return 0;
}
person Rickard    schedule 10.11.2009