Это старая версия документа.



Автор: Эли Дэ Брувэр (Elie De Brauwer)

В шестой части этой серии я показал вам несложные способы исследования приложений. В этой статье я представлю инструмент, который позволит вам копнуть глубже, сделать post-mortem анализ и изучить внутреннюю работу приложения. strace/ltrace/valgrind – действительно отличные инструменты, но они покажут вам только часть того, что происходит на самом деле; strace например, показывает только системные вызовы, а valgrind – что происходит с выделением/освобождением памяти. Инструмент, обсуждаемый здесь, называется gdb (The GNU debugger), и для него не существует пределов – если есть что-то, относящееся к приложению, которое вы хотите исследовать, GDB – это то, что вам нужно. На обычной Ubuntu-системе gdb может быть установлен командой:

sudo apt-get install gdb

Все IDE в Linux-системе с возможностью отладки обычно имеют текстовый режим с gdb в качестве основы. Здесь я сфокусируюсь на использовании gdb из командной строки, но знайте, что, когда дело доходит до комплексной отладки, иногда полезно иметь графическое представление. Одной из старейших графических надстроек над gdb является (Data Display Debugger), вы можете установить её, набрав:

sudo apt-get install ddd

Выше представлен скриншот ddd в действии. Он состоит из трёх больших панелей. Вверху находится панель данных, где вы можете вывести переменные и посмотреть их содержимое. В середине вы видите панель с исходным кодом – здесь можно установить точки остановки. Внизу находится панель взаимодействия с gdb. Здесь можно набрать любую команду gdb или нажать на соответствующие кнопки.

Пример для этой статьи называется ifstat. В Ubuntu уже существует приложение ifstat, наш пример ведёт себя так же, но он проще. Приложение представлено в Листинге 1 и в Листинге 2. Цель приложения – печатать каждые 2 секунды скорость передачи данных заданного сетевого устройства. В основе приложения – цикл while (Строки 29-49), в котором читается /proc/dev/net и печатается входящая и исходящая скорость потока заданного сетевого устройства в килобайтах в секунду и в пакетах в секунду. Функция main сама по себе довольно проста (Строки 51-60). Здесь мы проверяем, задан ли один параметр командной строки. Этот параметр станет интерфейсом, за которым мы хотим наблюдать. Если параметры отсутствуют, или их передано слишком много, ечатается сообщение с инструкциями пользователю, как использовать приложение. Пока ничего нового для нас, все новые штуки в функции parseDevFile() (Строки 5-28) будут кратко обсуждены ниже. Эта функция открывает /proc/dev/net и производит парсинг его содержимого; счётчики, которые представляют для насинтерес, будут сохранены в указателях bRx, pRx, bTx и pTx, которые передаются при вызове этой функции. Принимая указатели, мы можем изменить их значения внутри функции. Функция вернет 0 в случае успеха или -1, если произошёл сбой при открытии файла. <code>Листинг 1: ifstat.c

01. #include <stdio.h> 02. #include <stdlib.h> 03. #include <string.h> 04. #include <unistd.h> 05. typedef unsigned long long ull 06. int parseDevFile(const char * iface, ull *bRx, ull *pRx, 07. ull *bTx, ull *pTx) 08. { 09. FILE * fp = NULL; 10. char * line = NULL; 11. unsigned int len = 0; 12. fp = fopen(«/proc/net/dev», «r»); 13. if(fp==NULL) 14. { 15. return -1; 16. } 17. while(getline(&line,&len,fp)!= -1) 18. { 19. if(strstr(line,iface)!=NULL) 20. { 21.

  sscanf(strstr(line,":")+1,"%llu%llu%*u%*u%*u%*u%*u%*u%llu%llu",

22. bRx, pRx, bTx, pTx); 23. } 24. } 25. fclose(fp); 26. free(line); 27. return 0; 28. } 29. void dumpInterfaceUsage(const char * iface) 30. { 31. ull ifaceBRxOld=0, ifaceBTxOld=0, ifacePRxOld=0, ifacePTxOld=0; 32. ull ifaceBRxNew=0, ifaceBTxNew=0, ifacePRxNew=0, ifacePTxNew=0; 33. const int SLEEP_TIME = 2; 34. 35.

  if(parseDevFile(iface,&ifaceBRxOld,&ifacePRxOld,&ifaceBTxOld,&ifacePTx
  Old)==-1) return;

36. sleep(SLEEP_TIME); 37. while(1) 38. { 39.

  if(parseDevFile(iface,&ifaceBRxNew,&ifacePRxNew,&ifaceBTxNew,&ifac
  ePTxNew)==-1) return;

40. printf(«%s In: %8.2f kbyte/s %5llu P/s Out: %8.2f kbyte/s

  %5llu P/s\n", iface,

41. (ifaceBRxNew-ifaceBRxOld)/(SLEEP_TIME * 1024.0), 42. (ifacePRxNew-ifacePRxOld)/SLEEP_TIME, 43. (ifaceBTxNew-ifaceBTxOld)/(SLEEP_TIME * 1024.0), 44. (ifacePTxNew-ifacePTxOld)/SLEEP_TIME); 45. ifaceBRxOld=ifaceBRxNew; ifaceBTxOld=ifaceBTxNew; 46. ifacePRxOld=ifacePRxNew; ifacePTxOld=ifacePTxNew; 47. sleep(SLEEP_TIME); 48. } 49. } 50. 51. int main(int argc, char **argv) 52. { 53. if(argc != 2) 54. { 55. printf(«Использование: %s имяинтерфейса\n», argv[0]); 56. exit(1); 57. } 58. dumpInterfaceUsage(argv[1]); 59. return 0; 60. }