Средства тестирования приложений для разработчиков

       

Сообщения об ошибках и предупреждениях Настройка фильтра


Rational Purify по своей природе способна выловить не только ошибки, связанные с потерей памяти, но также ряд других не менее важных ошибок. Следует определить, что все сообщения делятся на две категории: ОШИБКИ и ПРЕДУПРЕЖДЕНИЯ. Во время запуска программа будет дотошно собирать все виды сообщений, и только настройка фильтра позволит отказаться от заведомо лишней, ненужной информации. Система фильтров Purify способна настроить не только уровень "придирчивости" к программе, но и количество исследуемых внешних модулей (чтобы разработчик мог концентрироваться только на собственных ошибках и не огорчался по поводу системных ошибок).

По умолчанию Purify выводит все сообщения и предупреждения, что может повергнуть разработчика в шок (даже в абсолютно правильной программе могут быть определенные предупреждения). Это связанно со спецификой поиска неточностей, так как некоторые предупреждения могут счиаться ошибкой, а могут и не быть таковыми - все зависит от конкретного алгоритма! Вот почему Purify и предлагает мощный фильтр

Как видно из рисунка, предполагается ставить фильтры либо по сообщениям (вручную) либо по категориям (при этом соответствующие сообщения выберутся автоматически). Обратите внимание на список сообщений: количество доходит до 41 и растет с каждой новой версией! Перед тем, как мы перейдем к рассмотрению всех сообщений, хочется отметить очень важный нюанс: Purify способен работать совместно с отладчиком (он прописывается отдельно). В этом случае возможна двойная работа по отладке программы с установкой брейкпоинтов:. итд.

Попробуем рассмотреть некоторые сообщения Purify c комментариями и примерами:

ABR: Array Bounds Read Выход за пределы массива при чтении #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = new int[2]; //Определить число элементов ptr[0] = 0; ptr[1] = 1; for (int i=0; i <= 2; i++) { //ОШИБКА //ABR when i is 2 cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; } delete[] ptr; return(0); }

ABW: Array Bounds Write Выход за пределы массива при записи #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = new int[2]; //Определить число элементов for (int i=0; i <= 2; i++) { //ОШИБКА ptr[i] = i; cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; //ABW + ABR when i is 2 } delete[] ptr; return(0); }

ABWL: Late Detect Array Bounds Write Cообщение указывает, что программа записала значение перед началом или после конца распределенного блока памяти #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = new int[2]; //Определить число элементов for (int i=0; i <= 2; i++) { //ОШИБКА ptr[i] = i; cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; } delete[] ptr; //ABWL: ОШИБКА return(0); }

BSR: Beyond Stack Read сообщение указывает, что функция в программе собирается читать вне текущего указателя вершины стека #include <windows.h> #include <iostream.h> #define A_LOT 256 int * setup_values(void) { int values[A_LOT]; //ОШИБКА: должен быть статичным for (int i=0; i < A_LOT; i++) { values[i] = i; } return(values); //ОШИБКА: неизвестно, что возвращать } int main(int, char **) { int *values; values = setup_values(); for (int i=0; i < A_LOT; i++) { //BSR: значения из "setup_values" больше не находятся в стеке cerr << "element #" << i << " is " << values[i] << '\n'; } return(0); }

BSW: Beyond Stack Write сообщение указывает, что функция в программе собирается писать вне текущего указателя вершины стека (пример см.


выше) FFM: Freeing Freed Memory Попытка освобождения свободного блока памяти #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr1 = new int; // int int *ptr2 = ptr1; //ОШИБКА: должен дублировать объект, а не копировать указатель *ptr1 = 10; *ptr2 = 20; cerr << "ptr1" << " is " << *ptr1 << '\n'; cerr << "ptr2" << " is " << *ptr2 << '\n'; delete ptr1; delete ptr2; //FFM: ОШИБКА return(0); } FIM: Freeing Invalid Memory Попытка освобождения некорректного блока памяти #include <iostream.h> int main(int, char **) { int i; delete[] &i; //FIM: не было операции new. Освобождать нечего! return(0); } FMM: Freeing Mismatched Memory Сообщение указывает, что программа пробует освобождать память с неправильным ВЫЗОВОМ API для того типа памяти #include <windows.h> int main(int, char **) { HANDLE heap1, heap2; heap1 = HeapCreate(0, 1000, 0); heap2 = HeapCreate(0, 1000, 0); int *pointer = (int *) HeapAlloc(heap1, 0, sizeof(int)); HeapFree(heap2, 0, pointer); //ОШИБКА: неправильное освобождение памяти HeapDestroy(heap1); HeapDestroy(heap2); return(0); } FMR: Free Memory Read Попытка чтения уже освобожденного блока памяти #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = new int[2]; ptr[0] = 0; ptr[1] = 1; delete[] ptr; //ОШИБКА: (специально сделано удаление) for (int i=0; i < 2; i++) { //FMR: ОШИБКА ДОСТУПА К ПАМЯТИ cerr << "element #" << i << " is " << ptr[i] << '\n'; } return(0); } FMW: Free Memory Write Попытка записи уже освобожденного блока памяти #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = new int[2]; ptr[0] = 0; ptr[1] = 1; delete[] ptr; //ОШИБКА: (специально сделано удаление) for (int i=0; i < 2; i++) { ptr[i] *= i; //FMR + FMW: потому что ptr уже удален cerr << "element #" << i << " is " << ptr[i] << '\n'; //FMR } return(0); } HAN: Invalid Handle Операции над неправильным дескриптором #include <iostream.h> #include <windows.h> #include <malloc.h> int main(int, char **) { (void) LocalUnlock((HLOCAL)3);//HAN: 3 - неправильный указатель return(0); } HIU: Handle In Use Индикация утечки ресурсов.


Неправильная индикация дескриптора #include <iostream.h> #include <windows.h> static long get_alignment(void) { SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); return(sys_info.dwAllocationGranularity); } int main(int, char **) { const long align = get_alignment(); HANDLE file_handle = CreateFile("file.txt", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { cerr << "ОШИБКА файла\n"; return(1); } HANDLE map_handle = CreateFileMapping(file_handle, NULL, PAGE_READWRITE, 0, align, "mymap"); if (map_handle == INVALID_HANDLE_VALUE) { cerr << "Unable to create actual mapping\n"; return(1); } char *pointer = (char *) MapViewOfFile(map_handle, FILE_MAP_WRITE, 0, 0, align); if (pointer == NULL) { cerr << " Unable to map into address space\n"; return(1); } strcpy(pointer, "hello\n"); //HIU: map_handle все еще доступный и правильный //HIU: file_handle все еще доступный и правильный return(0); } ILK: COM Interface Leak Утечка COM интерфейса #include <windows.h> int main(int, char **) { LPMALLOC lpIm; CoGetMalloc( MEMCTX_TASK, (LPMALLOC*)&lpIm); IMalloc_Release(lpIm); //НЕВЕРНЫЙ ЗАПРОС return(0); } IPR: Invalid Pointer Read Ошибка обращения к памяти, когда программа пытается произвести чтение из недоступной области #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = (int *) 0x80000000; //ОШИБКА: Указатель на зарезервированную часть адресного пространства for (int i=0; i < 2; i++) { //IPR: Попытка обращения к недопустимому указателю cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; } return(0); } IPW: Invalid Pointer Write Ошибка обращения к памяти, когда программа пытается произвести запись из недоступной области #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = (int *) 0x80000000; //ОШИБКА: Указатель на зарезервированную часть адресного пространства for (int i=0; i < 2; i++) { //IPW + IPR: Попытка обращения к недопустимому указателю ptr[i] = i; cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; } return(0); } MAF: Memory Allocation Failure Ошибка в запросе на распределение памяти #include <iostream.h> #include <windows.h> #define VERY_LARGE 3000000000 //Больше, чем можем получить int main(int, char **) { int *ptr = new int[VERY_LARGE / sizeof(int)]; //MAF: нельзя так много if (ptr == 0) { cerr << "Failed to alloc, as expected\n"; return (1); } else { cerr << "Got " << VERY_LARGE << " bytes @" << (unsigned long)ptr << '\n'; delete[] ptr; return(0); } } MLK: Memory Leak Утечка памяти #include <windows.h> #include <iostream.h> int main(int, char **) { (void) new char[1000]; (void) new char[1000]; (void) new char[1000]; (void) new char[1000]; (void) new char[1000]; //5 килобайт потерь return(0); } ИЛИ void All::OnAppAbout() { char *alex; //Указатель alex=(char *)malloc(20000); //MLK: берем, но не отдаем CAboutDlg aboutDlg; aboutDlg.DoModal(); } MPK: Potential Memory Leak Потенциальная утечка памяти (возникает когда производится операция над массивом не с нулевого элемента) #include <iostream.h> #include <windows.h> int main(int, char **) { static char *ptr = new char[500000]; ptr += 100; //MPK: обнаружится, как потенциально пропущенное return(0); } NPR: Null Pointer Read Попытка чтения с нулевого адреса #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = (int *) 0x0; //ОШИБКА for (int i=0; i < 2; i++) { //NPR: ошибка доступа cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; } return(0); } NPW: Null Pointer Write Попытка записи в нулевой адрес #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = (int *) 0x0; //ОШИБКА for (int i=0; i < 2; i++) { //NPW: ошибка доступа ptr[i] = i; cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; } return(0); } UMC: Uninitialized Memory Copy Попытка копирования непроинициализированного блока памяти #include <iostream.h> #include <windows.h> #include <string.h> int main(int, char **) { char *ptr = new char[10]; char var[10]; memcpy(var, ptr, 10); //UMC предупреждение delete[] ptr; return(0); } UMR: Uninitialized Memory Read Попытка чтения непроинициализированного блока памяти #include <iostream.h> #include <windows.h> int main(int, char **) { int *ptr = new int; cerr << "*ptr is " << *ptr << '\n'; //UMR: нет значения в ptr delete[] ptr; return(0); } Подведем итоги статьи описанием основных возможностей Purify:
  1. Отслеживание ошибок доступа к памяти
  2. Сбор и вывод статистики по использованию памяти
  3. Использование комплексного подхода к тщательному тестированию
  4. Технология OCI - Object Code Insertion позволяет детально отследить и выловить ошибку не только в контролируемом модуле, но и в модулях DLL сторонних разработчиков
  5. Тестирование ActiveX, COM/DCOM, ODBC, DLL
  6. Настраиваемый, двухуровневый способ тестирования (придирчивости) приложений
  7. Интеграция с Visual Studio
  8. Открытое API.Позволяет дописывать разработчикам собственные модули и присоединять их.
  9. Совместная работа с любым отладчиком
  10. Тестирование системных вызовов
Связаться с автором и получить дополнительную информацию можно по e-mail: См. также:  

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