Сообщения об ошибках и предупреждениях
Для того, чтобы иметь представление о возможностях продукта, опишем то, какие ошибки и потенциальные ошибки могут присутствовать в тестируемом приложение.
Отметим разницу между ошибкой и потенциальной ошибкой и опишем:
-
Ошибка — свершившийся факт нестабильной или некорректной работы приложения, проводящий к неадекватным действиям приложения или системы. Примером подобной ошибки можно считать выход за пределы массива или попытка записи данных по 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);
}
Содержание раздела