Тестирование софта - статьи

       

Сообщения об ошибках и предупреждениях


Для того, чтобы иметь представление о возможностях продукта, опишем то, какие ошибки и потенциальные ошибки могут присутствовать в тестируемом приложение.

Отметим разницу между ошибкой и потенциальной ошибкой и опишем:

  • Ошибка — свершившийся факт нестабильной или некорректной работы приложения, проводящий к неадекватным действиям приложения или системы. Примером подобной ошибки можно считать выход за пределы массива или попытка записи данных по 0 адресу;

  • Потенциальная ошибка — в приложении имеется фрагмент кода, который при нормальном исполнении не приводит к ошибкам. Ошибка возникает только в случае стечения обстоятельств, либо не проявляет себя никогда. К данной категории можно отнести такие особенности, как инициализация массива с ненулевого адреса, скажем, имеется массив на 100 байт, но каждый раз обращение к нему производится с 10 элемента. В этом случае Purify считает, что имеется потенциальная утечка памяти размером в 10 байт.

Естественно, что подобное поведение может быть вызвано спецификой приложения, например, так вести себя может текстовый редактор. Поэтому в Purify применятся деление информации на ошибки и потенциальные ошибки (которые можно воспринимать как специфику).

Список ошибок и потенциальных ошибок достаточно объемен и постоянно пополняется. Кратко опишем основные сообщения, выводимые после тестирования:

  • Array Bounds Read Выход за пределы массива при чтении;
  • Array Bounds Write Выход за пределы массива при записи;
  • Late Detect Array Bounds Write Cообщение указывает, что программа записала значение перед началом или после конца распределенного блока памяти;
  • Beyond Stack Read Сообщение указывает, что функция в программе собирается читать вне текущего указателя вершины стека;
  • Freeing Freed Memory Попытка освобождения свободного блока памяти;
  • Freeing Invalid Memory Попытка освобождения некорректного блока памяти;
  • Freeing Mismatched Memory Сообщение указывает, что программа пробует;
  • Free Memory Read Попытка чтения уже освобожденного блока памяти;
  • Free Memory Write Попытка записи уже освобожденного блока памяти;
  • Invalid Handle Операции над неправильным дескриптором;


    ptr[1] = ‘r’;
    ptr[2] = ‘r’;
    ptr[3] = ‘o’;
    ptr[4] = ‘r’;
    for (int i=0; i <= 5; i++) {
    //Ошибка, при i=5 – выход за пределы массива
    cerr << "ptr[" << i << "] == " << ptr[i] << '\n';
    }
    delete[] ptr;
    return(0);
    } ABW: Array Bounds Write. Выход за пределы массива при записи Вероятность того, что приложение может неадекватно вести себя из-за данной ошибки более высоко, так как запись по адресу, превышающим размер блока, вызывает исключение. Отметим, что память можно определить статически, массовом, как показано в примере. А можно динамически (например, выделив блок памяти по ходу исполнения приложения). По умолчанию, Purify успешно справляется только с динамическим распределением, четко выводя сообщение об ошибке. В случае статического распределения, все зависит от размеров «кучи» и настроек компилятора. #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char * ptr = new char[5];//Выделяем память под массив из 5символов
    for (int i=0; i <= 5; i++) {
    //Ошибка, при i=5 - выход за пределы массива
    ptr[i] = ‘!’;
    cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; //ABW + ABR when i is 5
    }
    delete[] ptr;
    return(0);
    } ABWL: Late Detect Array Bounds Write. Cообщение указывает, что программа записала значение перед началом или после конца распределенного блока памяти #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char * ptr = new char[5];//Выделяем память под массив из 5 символов
    for (int i=0; i <= 5; i++) {
    //Ошибка – попытка записи после блока выделенной памяти
    ptr[i] = ‘!’;
    cerr << "ptr[" << i << "] == " << ptr[i] << '\n';
    }
    delete[] ptr; //ABWL: ОШИБКА
    return(0);
    } BSR: Beyond Stack Read. Сообщение указывает, что функция в программе собирается читать вне текущего указателя вершины стека Категория: Stack Error #include <windows.h>
    #include <iostream.h>


    #define A_NUM 100
    char * create_block(void)
    {
    char block[A_NUM];//Ошибка: массив должен быть статическим
    for (int i=0; i < A_NUM; i++) {
    block[i] = ‘!’;
    }
    return(block);//Ошибка: неизвестно, что возвращать
    } int main(int, char **)
    {
    char * block;
    block = create_block();
    for (int i=0; i < A_NUM; i++) {
    //BSR: нет гарантии, что элементы из "create_block" до сих пор находятся в стеке cerr << "element #" << i << " is " << block[i] << '\n';
    }
    return(0);
    } FFM: Freeing Freed Memory. Попытка освобождения свободного блока памяти. В большом приложении трудно отследить момент распределения блока и момент освобождения. Очень часто методы реализуются разными разработчиками, и, соответственно, возможна ситуация, когда распределенный блок памяти освободается дважды в разных участках приложения. Категория: Allocations and deallocations #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char *ptr1 = new char;
    char *ptr2 = ptr1;//Ошибка: должен дублировать объект, а не копировать указатель
    *ptr1 = ‘a’;
    *ptr2 = ‘b’;
    cerr << "ptr1" << " is " << *ptr1 << '\n';
    cerr << "ptr2" << " is " << *ptr2 << '\n';
    delete ptr1;
    delete ptr2;//Ошибка – освобождение незанятой памяти
    return(0);
    } FIM: Freeing Invalid Memory. Попытка освобождения некорректного блока памяти. Разработчики часто путают простые статические значения и указатели, пытаясь освободить то, что не освобождается. Компилятор не всегда способен проанализировать и нейтрализовать данный вид ошибки. Категория: Allocations and deallocations #include <iostream.h> int main(int, char **)
    {
    char a;
    delete[] &a;//FIM: в динамической памяти нет объектов для уничтожения
    return(0);
    } FMM: Freeing Mismatched Memory. Сообщение указывает, что программа пробует освобождать память с неправильным ВЫЗОВОМ API для того типа памяти Категория: Allocations and deallocations #include <windows.h> int main(int, char **)


    {
    HANDLE heap_first, heap_second;
    heap_first = HeapCreate(0, 1000, 0);
    heap_second = HeapCreate(0, 1000, 0);
    char *pointer = (char *) HeapAlloc(heap_first, 0, sizeof(int));
    HeapFree(heap_second, 0, pointer);
    //Ошибка – во второй куче не выделялась память HeapDestroy(heap_first);
    HeapDestroy(heap_second);
    return(0);
    } FMR: Free Memory Read. Попытка чтения уже освобожденного блока памяти Все та же проблема с указателем. Блок распределен, освобожден, а потом, в ответ на событие, по указателю начинают записываться (или читаться) данные. Категория: Invalid pointers

    #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char *ptr = new char[2];
    ptr[0] = ‘!’;
    ptr[1] = ‘!’;
    delete[] ptr;//Ошибка – освобождение выделенной памяти
    for (int i=0; i < 2; i++) {
    //FMR: Ошибка- попытка чтения освобождённой памяти
    cerr << "element #" << i << " is " << ptr[i] << '\n';
    }
    return(0);
    } FMW: Free Memory Write. Попытка записи уже освобожденного блока памяти Категория: Invalid pointers #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char *ptr = new char[2];
    ptr[0] = ‘!’;
    ptr[1] = ‘!’;
    delete[] ptr;//специально освобождаем выделенную память
    for (int i=0; i < 2; i++) {
    ptr[i] *= ‘A’; //FMR + FMW: память для *ptr уже освобождена
    cerr << "element #" << i << " is " << ptr[i] << '\n'; //FMR
    }
    return(0);
    } HAN: Invalid Handle. Операции над неправильным дескриптором Категория: Invalid handles #include <iostream.h>
    #include <windows.h>
    #include <malloc.h>
    int main(int, char **)
    {
    int i=8;
    (void) LocalUnlock((HLOCAL)i);//HAN: i – не является описателем объекта памяти
    return(0);
    } HIU: Handle In Use. Индикация утечки ресурсов. Неправильная индикация дескриптора. #include <iostream.h>
    #include <windows.h>
    static long GetAlignment(void)
    {
    SYSTEM_INFO desc;
    GetSystemInfo(&desc);
    return(desc.dwAllocationGranularity);


    }
    int main(int, char **)
    {
    const long alignment = GetAlignment();
    HANDLE handleToFile = CreateFile("file.txt",
    GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, NULL);
    if (handleToFile == INVALID_HANDLE_VALUE) { cerr << "Ошибка открытия, создания файла\n";
    return(1);
    }
    HANDLE handleToMap = CreateFileMapping(handleToFile, NULL, PAGE_READWRITE, 0, alignment, "mapping_file");
    if (handleToMap == INVALID_HANDLE_VALUE) {
    cerr << "Unable to create actual mapping\n";
    return(1);
    }
    char * ptr = (char *) MapViewOfFile(handleToMap, FILE_MAP_WRITE, 0, 0, alignment); if (ptr == NULL) {
    cerr << " Unable to map into address space\n";
    return(1);
    }
    strcpy(ptr, "hello\n");
    //HIU: handleToMap до сих пор доступен и описывает существующий объект
    return(0);
    } IPR: Invalid Pointer Read. Ошибка обращения к памяти, когда программа пытается произвести чтение из недоступной области Категория: Invalid pointers #include <iostream.h> #include <windows.h>
    int main(int, char **)
    {
    char * pointer = (char *) 0xFFFFFFFF;
    //Ошибка - указатель на зарезервированную область памяти for (int i=0; i < 2; i++) {
    //IPR: обращение к зарезервированной части адресного пространства
    cerr << "pointer[" << i << "] == " << pointer[i] << '\n';
    }
    return(0);
    } IPW: Invalid Pointer Write. Ошибка обращения к памяти, когда программа пытается произвести запись из недоступной области Категория: Invalid pointers #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char *pointer = (char *) 0xFFFFFFFF;
    //Ошибка - указатель на зарезервированную область памяти for (int i=0; i < 2; i++) {
    //IPW + IPR: обращение к зарезервированной части адресного пространства
    pointer[i] = ‘!’;
    cerr << "ptr[" << i << "] == " << ptr[i] << '\n';
    }
    return(0);
    } MAF: Memory Allocation Failure. Ошибка в запросе на распределение памяти.


    Возникает в случаях, когда производится попытка распределить слишком большой блок памяти, например, когда исчерпан файл подкачки. Категория: Allocations and deallocations #include <iostream.h>
    #include <windows.h>
    #define BIG_BLOCK 3000000000 //размер блока
    int main(int, char **)
    {
    char *ptr = new char[BIG_BLOCK / sizeof(char)];
    //MAF: слишком большой размер для распределения
    if (ptr == 0) {
    cerr << "Failed to allocating, as expected\n";
    return (1);
    } else {
    cerr << "Got " << BIG_BLOCK << " bytes @" << (unsigned long)ptr << '\n';
    delete[] ptr;
    return(0);
    }
    } MLK: Memory Leak. Утечка памяти Распространенный вариант ошибки. Многие современные приложения грешат тем, что не отдают системе распределенные ресурсы по окончании своей работы. Категория: Memory leaks #include <windows.h>
    #include <iostream.h>
    int main(int, char **)
    {
    (void) new int[1000];
    (void) new int[1000];
    //результат потери памяти
    return(0);
    } MPK: Potential Memory Leak. Потенциальная утечка памяти
    Иногда возникает ситуация, в которой необходимо провести инициализацию массива не с нулевого элемента. Purify считает это ошибкой. Но разработчик, пишущий пресловутый текстовый редактор может инициализировать блок памяти не с нулевого элемента. Категория: Memory leaks #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    static int *pointer = new int[100000];
    pointer += 100;//MPK: потеряли начало массива
    return(0);
    } NPR: Null Pointer Read. Попытка чтения с нулевого адреса
    Бич всех программ на С\С++. Очень часто себя ошибка проявляет при динамическом распределении памяти приложением, так как не все разработчики ставят условие на получение блока памяти, и возникает ситуация, когда система не может выдать блок указанного размера и возвращает ноль. По причине отсутствия условия разработчик как не в чем не бывало начинает проводить операции над блоком, адрес в памяти которого 0. Категория: Invalid pointers #include <iostream.h>


    #include <windows.h>
    int
    main(int, char **)
    {
    char * pointer = (char *) 0x0; //указатель на нулевой адрес
    for (int i=0; i < 2; i++) {
    //NPR: попытка чтения с нулевого адреса
    cerr << "pointer[" << i << "] == " << pointer[i] << '\n';
    }
    return(0);
    } NPW: Null Pointer Write. Попытка записи в нулевой адрес Категория: Invalid pointers #include <iostream.h>
    #include <windows.h>
    int
    main(int, char **)
    {
    char * pointer = (char *) 0x0; //указатель на нулевой адрес
    for (int i=0; i < 2; i++) {
    //NPW: ошибка доступа
    pointer[i]=’!’;
    cerr << "pointer[" << i << "] == " << pointer[i] << '\n';
    }
    return(0);
    } UMC: Uninitialized Memory Copy. Попытка копирования непроинициализированного блока памяти Категория: Unitialized memory #include <iostream.h>
    #include <windows.h>
    #include <string.h> int main(int, char **)
    {
    int * pointer = new int[10];
    int block[10];
    for (int i=0; i<10;i++)
    {
    pointer[i]=block[i]; //UMC предупреждение
    cerr<<block[i]<<”\n”; }

    delete[] pointer;
    return(0);
    } UMR: Uninitialized Memory Read. Попытка чтения непроинициализированного блока памяти Категория: Unitialized memory #include <iostream.h>
    #include <windows.h>
    int main(int, char **)
    {
    char *pointer = new char;
    cerr << "*pointer is " << *pointer << '\n';
    //UMR: pointer указывает на непроинициализированный элемент
    delete[] pointer;
    return(0);
    }

    Содержание раздела