From 7099baac537b9e6456f4bb7faa6fda15e130c7ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= <krh@bitplanet.net>
Date: Tue, 1 Dec 2009 16:12:48 -0500
Subject: [PATCH] Handle wrap-around in perf event buffer

This avoids the triple-mmap trick, which should work, but the third
mmap returns EINVAL on my system.  This patch just handles the wrap-around
case by copying the event in question to a temp buffer.
---
 collector.c |   71 +++++++++++++++++++++++++---------------------------------
 1 files changed, 31 insertions(+), 40 deletions(-)

diff --git a/collector.c b/collector.c
index 6f5abe2..e98eac3 100644
--- a/collector.c
+++ b/collector.c
@@ -211,7 +211,8 @@ static void
 on_read (gpointer data)
 {
     counter_t *counter = data;
-    int mask = (N_PAGES * get_page_size() - 1);
+    int size = N_PAGES * get_page_size();
+    int mask = size - 1, part;
     gboolean skip_samples;
     Collector *collector;
     uint64_t head, tail;
@@ -250,6 +251,9 @@ on_read (gpointer data)
     while (head - tail >= sizeof (struct perf_event_header))
     {	
 	struct perf_event_header *header = (void *)(counter->data + (tail & mask));
+	counter_event_t *e;
+	uint32_t buffer[256];
+	static int biggest = 0;
 
 	if (header->size > head - tail)
 	{
@@ -265,7 +269,27 @@ on_read (gpointer data)
 	    if (header->type == PERF_EVENT_SAMPLE)
 		collector->n_samples++;
 	    
-	    process_event (collector, counter, (counter_event_t *)header);
+	    if ((tail & mask) + header->size <= size)
+	    {
+		e = (counter_event_t *) header;
+		process_event (collector, counter, e);
+	    }
+	    else
+	    {
+		if (header->size > sizeof buffer)
+		    e = g_malloc(header->size);
+		else
+		    e = (counter_event_t *) buffer;
+
+		part = size - (tail & mask);
+		memcpy(e, header, part);
+		memcpy((char *) e + part, counter->data, header->size - part);
+		process_event (collector, counter, e);
+
+		if (e != (counter_event_t *) buffer)
+		    g_free(e);
+	    }
+
 	}
 	
 	tail += header->size;
@@ -295,41 +319,6 @@ fail (GError **err, const char *what)
 
     return NULL;
 }
-	
-static void *
-map_buffer (counter_t *counter, GError **err)
-{ 
-    int n_bytes = N_PAGES * get_page_size();
-    void *address, *a;
-
-    /* We map the ring buffer twice in consecutive address space,
-     * so that we don't need special-case code to deal with wrapping.
-     */
-    address = mmap (NULL, n_bytes * 2 + get_page_size(), PROT_NONE,
-		    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-
-    if (address == MAP_FAILED)
-	return fail (err, "mmap");
-    
-    a = mmap (address + n_bytes, n_bytes + get_page_size(),
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED | MAP_FIXED, counter->fd, 0);
-    
-    if (a != address + n_bytes)
-	return fail (err, "mmap");
-
-    a = mmap (address, n_bytes + get_page_size(),
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED | MAP_FIXED, counter->fd, 0);
-
-    if (a == MAP_FAILED || a != address)
-	return fail (err, "mmap");
-
-    if (a != address)
-	return fail (err, "mmap");
-
-    return address;
-}
 
 static counter_t *
 counter_new (Collector  *collector,
@@ -376,10 +365,12 @@ counter_new (Collector  *collector,
     counter->collector = collector;
     counter->fd = fd;
 
-    counter->mmap_page = map_buffer (counter, err);
+    counter->mmap_page = 
+	    mmap (NULL, (N_PAGES + 1) * get_page_size(),
+		  PROT_READ | PROT_WRITE, MAP_SHARED, counter->fd, 0);
     
-    if (!counter->mmap_page || counter->mmap_page == MAP_FAILED)
-	return NULL;
+    if (counter->mmap_page == MAP_FAILED)
+	return fail (err, "mmap");
     
     counter->data = (uint8_t *)counter->mmap_page + get_page_size ();
     counter->tail = 0;
-- 
1.6.5.rc2

