Использование perf_event_open для мониторинга контейнеров Docker - PullRequest
0 голосов
/ 19 октября 2018

Я написал программу на C для извлечения событий производительности, таких как циклы ЦП контейнеров Docker.Я имею в виду программу пространства пользователя на уровне хоста (мониторинг на уровне хоста, а не внутри докера).Я даю pid контейнера докера как запись pid perf_event_open () , однако, я всегда имею 0 в качестве возвращаемого значения.Я протестировал программу для других не-докеров, например Firefox, и она работает очень хорошо.

Я установил PERF_FLAG_PID_CGROUP в качестве флага, ничего не меняется!Вот код:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>

    static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags)
   {
       int ret;

       ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
                      group_fd, flags);
       return ret;
   }

   int
   main(int argc, char **argv)
   {
       struct perf_event_attr pe;
       long long count;
       int fd;

       fd = open("/sys/fs/cgroup/perf_event/docker/f42c13cd9dd700544fe670e30d0b3216bdceaf01ddc370405618fdecfd10b26d", O_RDONLY);
       if (fd == -1)
          return 0;

       memset(&pe, 0, sizeof(struct perf_event_attr));
       pe.type = PERF_TYPE_HARDWARE;
       pe.size = sizeof(struct perf_event_attr);
       pe.config = PERF_COUNT_HW_CPU_CYCLES;
       pe.disabled = 1;
       pe.exclude_kernel = 0;
       pe.exclude_hv = 0;

       fd = perf_event_open(&pe, fd, -1, -1, PERF_FLAG_PID_CGROUP);
       if (fd == -1) {
          fprintf(stderr, "Error opening leader %llx\n", pe.config);
          exit(EXIT_FAILURE);
       }

       ioctl(fd, PERF_EVENT_IOC_RESET, 0);
       ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);

       usleep(100);

       ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
       read(fd, &count, sizeof(long long));

       printf("Used %lld instructions\n", count);

       close(fd);
   }

В соответствии с Man-страницей perf_event_open (), я также даю файл fd, открытый в каталоге контейнера docker в groupfs.Не работает!

Не могли бы вы помочь мне решить проблему?Спасибо

Обновление: Я проверял другие события, например, PERF_COUNT_HW_CACHE_REFERENCES, я вижу 0 как возвращаемое значение!

ОС: Ubuntu 16.04

Ядро: 4.15.0-041500-generic

Архитектура: X86_64

1 Ответ

0 голосов
/ 21 октября 2018

Вы не указали, какую версию ядра Linux вы используете.Я буду основывать свой ответ в соответствии с последней версией ядра Linux.

Параметры, которые вы передали системному вызову perf_event_open, выглядят правильно, за исключением одного.

В вашем случае вы передаете cpu = -1 в качестве параметра для perf_event_open.

Хотя обычно это работает при обычной фильтрации perf event (т. Е. На процессор или на поток), передача cpu = -1 не будет работать при фильтрации на основе cgroup для perf.В режиме cgroup аргумент pid используется для передачи открытого fd в каталог cgroup в cgroupfs (который вы, кажется, правильно передали).Аргумент cpu обозначает процессор, на котором можно отслеживать потоки из этой группы.И когда cpu=-1 это означает, что perf event измеряет указанный процесс / поток на любом ЦП (без учета того, принадлежит ли этот ЦП к группе, с которой вы измеряете).

Это как это изображено в последнем коде Linux.

if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1)) return -EINVAL;

Вы можете ясно видеть, что если либо PID=-1 or CPU=-1, то метод syscall вернет ошибку.


Рабочий пример

Из документации perf_event_open совершенно ясно, что -

cgroup monitoring is available only for system-wide events and may therefore require extra permissions.

Поскольку мы делаем cgroupМониторинг в этом случае, который уже понимается, когда мы наблюдаем за perf-events контейнера, нам придется отслеживать событие всей системы.Это означает, что мониторинг происходит для ALL доступных процессоров в системе.

Рабочий код

В моей системе 4 ядра, и поэтому я использую процессоры - 0,1, 2,3

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#include <errno.h>

    static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags)
   {
       int ret;

       ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
                      group_fd, flags);
       return ret;
   }

   int
   main(int argc, char **argv)
   {
       struct perf_event_attr pe;
       long long count, count1, count2, count3;
       int fd, fd1, fd2, fd3, fd4;

       fd1 = open("/sys/fs/cgroup/perf_event/docker/001706b1a71617b0ce9d340f706d901e00ee398091dd62aded2a1863fc8c274a", O_RDONLY);
       if (fd1 == -1)
          return 0;

       memset(&pe, 0, sizeof(struct perf_event_attr));
       pe.type = PERF_TYPE_HARDWARE;
       pe.size = sizeof(struct perf_event_attr);
       pe.config = PERF_COUNT_HW_INSTRUCTIONS;
       pe.disabled = 1;
       pe.exclude_kernel = 0;
       pe.exclude_hv = 0;

       fd = perf_event_open(&pe, fd1, 0, -1, PERF_FLAG_PID_CGROUP|PERF_FLAG_FD_CLOEXEC);
       if (fd == -1) {
          fprintf(stderr, "Error opening leader: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
       }
       fd2 = perf_event_open(&pe, fd1, 1, -1, PERF_FLAG_PID_CGROUP|PERF_FLAG_FD_CLOEXEC);
       if (fd2 == -1) {
          fprintf(stderr, "Error: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
       }
       fd3 = perf_event_open(&pe, fd1, 2, -1, PERF_FLAG_PID_CGROUP|PERF_FLAG_FD_CLOEXEC);
       if (fd3 == -1) {
          fprintf(stderr, "Error: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
       } 
       fd4 = perf_event_open(&pe, fd1, 3, -1, PERF_FLAG_PID_CGROUP|PERF_FLAG_FD_CLOEXEC);
       if (fd4 == -1) {
          fprintf(stderr, "Error: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
       }

       ioctl(fd, PERF_EVENT_IOC_RESET, 0);
       ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);

       ioctl(fd2, PERF_EVENT_IOC_RESET, 0);
       ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0);

       ioctl(fd3, PERF_EVENT_IOC_RESET, 0);
       ioctl(fd3, PERF_EVENT_IOC_ENABLE, 0);

       ioctl(fd4, PERF_EVENT_IOC_RESET, 0);
       ioctl(fd4, PERF_EVENT_IOC_ENABLE, 0);

       sleep(10);   // using sleep(10) to actually observe instructions

       ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
       ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0);
       ioctl(fd3, PERF_EVENT_IOC_DISABLE, 0);
       ioctl(fd4, PERF_EVENT_IOC_DISABLE, 0);

       read(fd, &count, sizeof(long long));
       read(fd2, &count1, sizeof(long long));
       read(fd3, &count2, sizeof(long long));
       read(fd4, &count3, sizeof(long long));

       printf("Used %lld instructions\n", count+count1+count2+count3);

       close(fd);
       close(fd2);
       close(fd3);
       close(fd4);
  }

`

Выход: Used 55174 instructions

...