perf cs-etm: Add support for multiple traceID queues
[linux-2.6-microblaze.git] / tools / perf / util / cs-etm.c
index 2483293..afc2491 100644 (file)
@@ -29,6 +29,7 @@
 #include "thread.h"
 #include "thread_map.h"
 #include "thread-stack.h"
+#include <tools/libc_compat.h>
 #include "util.h"
 
 #define MAX_TIMESTAMP (~0ULL)
@@ -82,7 +83,9 @@ struct cs_etm_queue {
        u64 offset;
        const unsigned char *buf;
        size_t buf_len, buf_used;
-       struct cs_etm_traceid_queue *traceid_queues;
+       /* Conversion between traceID and index in traceid_queues array */
+       struct intlist *traceid_queues_list;
+       struct cs_etm_traceid_queue **traceid_queues;
 };
 
 static int cs_etm__update_queues(struct cs_etm_auxtrace *etm);
@@ -208,31 +211,71 @@ out:
 static struct cs_etm_traceid_queue
 *cs_etm__etmq_get_traceid_queue(struct cs_etm_queue *etmq, u8 trace_chan_id)
 {
-       struct cs_etm_traceid_queue *tidq;
+       int idx;
+       struct int_node *inode;
+       struct intlist *traceid_queues_list;
+       struct cs_etm_traceid_queue *tidq, **traceid_queues;
        struct cs_etm_auxtrace *etm = etmq->etm;
 
-       if (!etm->timeless_decoding)
-               return NULL;
+       if (etm->timeless_decoding)
+               trace_chan_id = CS_ETM_PER_THREAD_TRACEID;
 
-       tidq = etmq->traceid_queues;
+       traceid_queues_list = etmq->traceid_queues_list;
 
-       if (tidq)
-               return tidq;
+       /*
+        * Check if the traceid_queue exist for this traceID by looking
+        * in the queue list.
+        */
+       inode = intlist__find(traceid_queues_list, trace_chan_id);
+       if (inode) {
+               idx = (int)(intptr_t)inode->priv;
+               return etmq->traceid_queues[idx];
+       }
 
+       /* We couldn't find a traceid_queue for this traceID, allocate one */
        tidq = malloc(sizeof(*tidq));
        if (!tidq)
                return NULL;
 
        memset(tidq, 0, sizeof(*tidq));
 
+       /* Get a valid index for the new traceid_queue */
+       idx = intlist__nr_entries(traceid_queues_list);
+       /* Memory for the inode is free'ed in cs_etm_free_traceid_queues () */
+       inode = intlist__findnew(traceid_queues_list, trace_chan_id);
+       if (!inode)
+               goto out_free;
+
+       /* Associate this traceID with this index */
+       inode->priv = (void *)(intptr_t)idx;
+
        if (cs_etm__init_traceid_queue(etmq, tidq, trace_chan_id))
                goto out_free;
 
-       etmq->traceid_queues = tidq;
+       /* Grow the traceid_queues array by one unit */
+       traceid_queues = etmq->traceid_queues;
+       traceid_queues = reallocarray(traceid_queues,
+                                     idx + 1,
+                                     sizeof(*traceid_queues));
+
+       /*
+        * On failure reallocarray() returns NULL and the original block of
+        * memory is left untouched.
+        */
+       if (!traceid_queues)
+               goto out_free;
+
+       traceid_queues[idx] = tidq;
+       etmq->traceid_queues = traceid_queues;
 
-       return etmq->traceid_queues;
+       return etmq->traceid_queues[idx];
 
 out_free:
+       /*
+        * Function intlist__remove() removes the inode from the list
+        * and delete the memory associated to it.
+        */
+       intlist__remove(traceid_queues_list, inode);
        free(tidq);
 
        return NULL;
@@ -412,6 +455,44 @@ static int cs_etm__flush_events(struct perf_session *session,
        return cs_etm__process_timeless_queues(etm, -1);
 }
 
+static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
+{
+       int idx;
+       uintptr_t priv;
+       struct int_node *inode, *tmp;
+       struct cs_etm_traceid_queue *tidq;
+       struct intlist *traceid_queues_list = etmq->traceid_queues_list;
+
+       intlist__for_each_entry_safe(inode, tmp, traceid_queues_list) {
+               priv = (uintptr_t)inode->priv;
+               idx = priv;
+
+               /* Free this traceid_queue from the array */
+               tidq = etmq->traceid_queues[idx];
+               thread__zput(tidq->thread);
+               zfree(&tidq->event_buf);
+               zfree(&tidq->last_branch);
+               zfree(&tidq->last_branch_rb);
+               zfree(&tidq->prev_packet);
+               zfree(&tidq->packet);
+               zfree(&tidq);
+
+               /*
+                * Function intlist__remove() removes the inode from the list
+                * and delete the memory associated to it.
+                */
+               intlist__remove(traceid_queues_list, inode);
+       }
+
+       /* Then the RB tree itself */
+       intlist__delete(traceid_queues_list);
+       etmq->traceid_queues_list = NULL;
+
+       /* finally free the traceid_queues array */
+       free(etmq->traceid_queues);
+       etmq->traceid_queues = NULL;
+}
+
 static void cs_etm__free_queue(void *priv)
 {
        struct cs_etm_queue *etmq = priv;
@@ -419,14 +500,8 @@ static void cs_etm__free_queue(void *priv)
        if (!etmq)
                return;
 
-       thread__zput(etmq->traceid_queues->thread);
        cs_etm_decoder__free(etmq->decoder);
-       zfree(&etmq->traceid_queues->event_buf);
-       zfree(&etmq->traceid_queues->last_branch);
-       zfree(&etmq->traceid_queues->last_branch_rb);
-       zfree(&etmq->traceid_queues->prev_packet);
-       zfree(&etmq->traceid_queues->packet);
-       zfree(&etmq->traceid_queues);
+       cs_etm__free_traceid_queues(etmq);
        free(etmq);
 }
 
@@ -497,19 +572,21 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
        u8  cpumode;
        u64 offset;
        int len;
-       struct   thread *thread;
-       struct   machine *machine;
-       struct   addr_location al;
-
-       (void)trace_chan_id;
+       struct thread *thread;
+       struct machine *machine;
+       struct addr_location al;
+       struct cs_etm_traceid_queue *tidq;
 
        if (!etmq)
                return 0;
 
        machine = etmq->etm->machine;
        cpumode = cs_etm__cpu_mode(etmq, address);
+       tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
+       if (!tidq)
+               return 0;
 
-       thread = etmq->traceid_queues->thread;
+       thread = tidq->thread;
        if (!thread) {
                if (cpumode != PERF_RECORD_MISC_KERNEL)
                        return 0;
@@ -545,6 +622,10 @@ static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm)
        if (!etmq)
                return NULL;
 
+       etmq->traceid_queues_list = intlist__new(NULL);
+       if (!etmq->traceid_queues_list)
+               goto out_free;
+
        /* Use metadata to fill in trace parameters for trace decoder */
        t_params = zalloc(sizeof(*t_params) * etm->num_cpu);
 
@@ -579,6 +660,7 @@ static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm)
 out_free_decoder:
        cs_etm_decoder__free(etmq->decoder);
 out_free:
+       intlist__delete(etmq->traceid_queues_list);
        free(etmq);
 
        return NULL;
@@ -1280,8 +1362,9 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
                                 struct cs_etm_packet *packet,
                                 u64 end_addr)
 {
-       u16 instr16;
-       u32 instr32;
+       /* Initialise to keep compiler happy */
+       u16 instr16 = 0;
+       u32 instr32 = 0;
        u64 addr;
 
        switch (packet->isa) {