2017-02-07 1 views
1

est-ce que quelqu'un sait comment configurer perf_event_attr struct qui peut déclencher PMU pour surveiller plusieurs (type) événement via perf_event_open()?perf_event_open - comment surveiller plusieurs événements

Comme perf record -e cycles,faults ls, il a deux types d'événements différents (PERF_TYPE_HARDWARE et PERF_TYPE_SOFTWARE), mais dans l'exemple sur perf_event_open's manpage, perf_event_attr.type ne peut seule valeur assignée.

Toute suggestion sera appréciée, merci!

20170208 Mise à jour Merci pour @gudok me pointant une direction, mais le résultat semble un peu anormal. programme de démonstration comme ci-dessous (pour le cycle de CPU de toute mesure système et miss cache):

#define _GNU_SOURCE 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/syscall.h> 
#include <string.h> 
#include <sys/ioctl.h> 
#include <linux/perf_event.h> 
#include <linux/hw_breakpoint.h> 
#include <asm/unistd.h> 
#include <errno.h> 
#include <stdint.h> 
#include <inttypes.h> 
#include <time.h> 

struct read_format { 
    uint64_t nr; 
    struct { 
    uint64_t value; 
    uint64_t id; 
    } values[]; 
}; 

int main(int argc, char* argv[]) { 
    struct perf_event_attr pea; 
    int fd1, fd2; 
    uint64_t id1, id2; 
    uint64_t val1, val2; 
    char buf[4096]; 
    struct read_format* rf = (struct read_format*) buf; 
    int i,j; 
    struct timespec time, time2; 

    time.tv_sec = 1; 
    time.tv_nsec = 0; 

    memset(&pea, 0, sizeof(struct perf_event_attr)); 
    pea.type = PERF_TYPE_HARDWARE; 
    pea.size = sizeof(struct perf_event_attr); 
    pea.config = PERF_COUNT_HW_CPU_CYCLES; 
    pea.disabled = 1; 
    pea.exclude_kernel = 1; 
    pea.exclude_hv = 1; 
    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 
    fd1 = syscall(__NR_perf_event_open, &pea, 0, -1, -1, 0); 
    ioctl(fd1, PERF_EVENT_IOC_ID, &id1); 

    memset(&pea, 0, sizeof(struct perf_event_attr)); 
    pea.type = PERF_TYPE_HARDWARE; 
    pea.size = sizeof(struct perf_event_attr); 
    pea.config = PERF_COUNT_HW_CACHE_MISSES; 
    pea.disabled = 1; 
    pea.exclude_kernel = 1; 
    pea.exclude_hv = 1; 
    pea.precise_ip = 2; // want to using PEBS 
    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 
    fd2 = syscall(__NR_perf_event_open, &pea, 0, -1, fd1 /*!!!*/, 0); 
    ioctl(fd2, PERF_EVENT_IOC_ID, &id2); 

    ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); 
    ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); 
    while (1) { 
    nanosleep(&time, &time2); 

    //ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); 

    read(fd1, buf, sizeof(buf)); 
    for (i = 0; i < rf->nr; i++) { 
     if (rf->values[i].id == id1) { 
     val1 = rf->values[i].value; 
     } else if (rf->values[i].id == id2) { 
     val2 = rf->values[i].value; 
     } 
    } 

    printf("cpu cycles: %"PRIu64"\n", val1); 
    printf("cache misses: %"PRIu64"\n", val2); 

    } 

    return 0; 
} 

Et la sortie est:

cpu cycles: 120 // Just have about 120 CPU cycles in a second 
cache misses: 0 // and doesn't have any cache miss? 
cpu cycles: 233 
cache misses: 0 
cpu cycles: 352 
cache misses: 0 
cpu cycles: 455 
cache misses: 0 
cpu cycles: 562 
cache misses: 0 
cpu cycles: 673 
cache misses: 0 
cpu cycles: 794 
cache misses: 0 
cpu cycles: 907 
cache misses: 0 
cpu cycles: 1011 
cache misses: 0 
cpu cycles: 1129 
cache misses: 3 
cpu cycles: 1269 
cache misses: 4 
cpu cycles: 1423 

Répondre

5

C'est un peu difficile.

Nous créons le premier compteur comme d'habitude. En outre, nous passons PERF_FORMAT_GROUP et PERF_FORMAT_ID pour pouvoir travailler avec plusieurs compteurs simultanément. Ce compteur sera notre chef de groupe.

struct perf_event_attr pea; 
int fd1, fd2; 
uint64_t id1, id2; 

memset(&pea, 0, sizeof(struct perf_event_attr)); 
pea.type = PERF_TYPE_HARDWARE; 
pea.size = sizeof(struct perf_event_attr); 
pea.config = PERF_COUNT_HW_CPU_CYCLES; 
pea.disabled = 1; 
pea.exclude_kernel = 1; 
pea.exclude_hv = 1; 
pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 
fd1 = syscall(__NR_perf_event_open, &pea, 0, -1, -1, 0); 

Ensuite, nous récupérer identifiant pour le premier compteur:

ioctl(fd1, PERF_EVENT_IOC_ID, &id1); 

deuxième (et tous les autres compteurs) sont créés de la même façon avec une seule exception: nous passons fd1 valeur comme argument de chef de groupe :

memset(&pea, 0, sizeof(struct perf_event_attr)); 
pea.type = PERF_TYPE_SOFTWARE; 
pea.size = sizeof(struct perf_event_attr); 
pea.config = PERF_COUNT_SW_PAGE_FAULTS; 
pea.disabled = 1; 
pea.exclude_kernel = 1; 
pea.exclude_hv = 1; 
pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 
fd2 = syscall(__NR_perf_event_open, &pea, 0, -1, fd1, 0); // <-- here 
ioctl(fd2, PERF_EVENT_IOC_ID, &id2); 

Ensuite, nous devons déclarer une structure de données pour lire plusieurs compteurs à la fois. Vous devez déclarer différents ensembles de champs en fonction des indicateurs que vous passez à perf_event_open. La page Manual mentionne tous les champs possibles. Dans notre cas, nous avons passé le drapeau PERF_FORMAT_ID qui ajoute le champ id. Cela nous permettra de faire la distinction entre différents compteurs.

struct read_format { 
    uint64_t nr; 
    struct { 
     uint64_t value; 
     uint64_t id; 
    } values[/*2*/]; 
}; 

Maintenant, nous appelons ioctls standard de profilage:

ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); 
ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); 
do_something(); 
ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); 

Enfin, nous lisons les compteurs de descripteur de fichier de chef de groupe. Les deux compteurs sont retournés dans structure unique read_format que nous déclarions:

char buf[4096]; 
struct read_format* rf = (struct read_format*) buf; 
uint64_t val1, val2; 

read(fd1, buf, sizeof(buf)); 
for (i = 0; i < rf->nr; i++) { 
    if (rf->values[i].id == id1) { 
    val1 = rf->values[i].value; 
    } else if (rf->values[i].id == id2) { 
    val2 = rf->values[i].value; 
    } 
} 
printf("cpu cycles: %"PRIu64"\n", val1); 
printf("page faults: %"PRIu64"\n", val2); 

Ci-dessous la liste complète du programme:

#define _GNU_SOURCE 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/syscall.h> 
#include <string.h> 
#include <sys/ioctl.h> 
#include <linux/perf_event.h> 
#include <linux/hw_breakpoint.h> 
#include <asm/unistd.h> 
#include <errno.h> 
#include <stdint.h> 
#include <inttypes.h> 

struct read_format { 
    uint64_t nr; 
    struct { 
    uint64_t value; 
    uint64_t id; 
    } values[]; 
}; 

void do_something() { 
    int i; 
    char* ptr; 

    ptr = malloc(100*1024*1024); 
    for (i = 0; i < 100*1024*1024; i++) { 
    ptr[i] = (char) (i & 0xff); // pagefault 
    } 
    free(ptr); 
} 

int main(int argc, char* argv[]) { 
    struct perf_event_attr pea; 
    int fd1, fd2; 
    uint64_t id1, id2; 
    uint64_t val1, val2; 
    char buf[4096]; 
    struct read_format* rf = (struct read_format*) buf; 
    int i,j; 

    memset(&pea, 0, sizeof(struct perf_event_attr)); 
    pea.type = PERF_TYPE_HARDWARE; 
    pea.size = sizeof(struct perf_event_attr); 
    pea.config = PERF_COUNT_HW_CPU_CYCLES; 
    pea.disabled = 1; 
    pea.exclude_kernel = 1; 
    pea.exclude_hv = 1; 
    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 
    fd1 = syscall(__NR_perf_event_open, &pea, 0, -1, -1, 0); 
    ioctl(fd1, PERF_EVENT_IOC_ID, &id1); 

    memset(&pea, 0, sizeof(struct perf_event_attr)); 
    pea.type = PERF_TYPE_SOFTWARE; 
    pea.size = sizeof(struct perf_event_attr); 
    pea.config = PERF_COUNT_SW_PAGE_FAULTS; 
    pea.disabled = 1; 
    pea.exclude_kernel = 1; 
    pea.exclude_hv = 1; 
    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 
    fd2 = syscall(__NR_perf_event_open, &pea, 0, -1, fd1 /*!!!*/, 0); 
    ioctl(fd2, PERF_EVENT_IOC_ID, &id2); 


    ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); 
    ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); 
    do_something(); 
    ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); 


    read(fd1, buf, sizeof(buf)); 
    for (i = 0; i < rf->nr; i++) { 
    if (rf->values[i].id == id1) { 
     val1 = rf->values[i].value; 
    } else if (rf->values[i].id == id2) { 
     val2 = rf->values[i].value; 
    } 
    } 

    printf("cpu cycles: %"PRIu64"\n", val1); 
    printf("page faults: %"PRIu64"\n", val2); 

    return 0; 
} 
+0

Merci pour votre réponse utile :) Il fonctionne, mais le résultat semble un peu inhabituel .. Je veux compter les cycles CPU par seconde, et il montre environ 120 cycles. La méthode que j'ai utilisée est-elle incorrecte? (Programme de démonstration mis à jour dans ma question) Merci encore. –

+1

@KevinChi, les cycles de cpu ne seront pas comptés pendant les appels de sommeil. Le temps CPU pendant le sommeil n'appartient pas au processus en cours.À l'intérieur de 'do_something', vous devez créer une boucle factice de 1-2 bln. itérations et compte 1) cycles cpu 2) temps nécessaire à l'exécution de cette boucle ('clock_gettime (CLOCK_THREAD_CPUTIME_ID)'). En divisant ces deux valeurs vous obtiendrez des cycles de cpu par seconde. Vous pouvez également configurer une minuterie d'une seconde et exécuter une boucle infinie jusqu'à ce qu'elle se déclenche. – gudok

+0

Oh, maintenant. J'ai utilisé la mauvaise méthode! –