From bbf26927e2b67aaa68dbc60380d77b7d81898938 Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@gmail.com>
Date: Sun, 30 Jan 2011 15:24:15 -0500
Subject: Store profile data in thread-local dictionaries.

---
 libdimension/profile.c | 178 +++++++++++++++++++++++++++++++++++++------------
 1 file changed, 135 insertions(+), 43 deletions(-)

diff --git a/libdimension/profile.c b/libdimension/profile.c
index 5e34931..337a025 100644
--- a/libdimension/profile.c
+++ b/libdimension/profile.c
@@ -28,55 +28,136 @@
 #include <stdio.h>
 #include <inttypes.h>
 
-/** Info for one predicted branch. */
+/** Information on one predicted branch. */
 typedef struct {
   char *location;
   uint64_t predicted, branches;
-} dmnsn_profile_info;
+} dmnsn_branch;
 
 /** Count of mispredicted branches. */
-static dmnsn_dictionary *dmnsn_profile_data = NULL;
-/** Mutex which protects \c dmnsn_profile_data. */
+static dmnsn_dictionary *dmnsn_profile = NULL;
+/** Mutex which protects \c dmnsn_profile. */
 static pthread_mutex_t dmnsn_profile_mutex = PTHREAD_MUTEX_INITIALIZER;
 
+/** Thread-local count of mispredicted branches. */
+static pthread_key_t dmnsn_thread_profile;
+/** Initialize the thread-specific pointer exactly once. */
+static pthread_once_t dmnsn_thread_profile_once = PTHREAD_ONCE_INIT;
+
+/** Add thread-specific profile data to the global counts. */
+static void
+dmnsn_profile_globalize(void *ptr)
+{
+  dmnsn_branch *branch = ptr;
+  if (dmnsn_profile) {
+    dmnsn_branch *global
+      = dmnsn_dictionary_at(dmnsn_profile, branch->location);
+    if (global) {
+      global->predicted += branch->predicted;
+      global->branches  += branch->branches;
+      dmnsn_free(branch->location);
+    } else {
+      dmnsn_dictionary_insert(dmnsn_profile, branch->location,
+                              branch);
+    }
+  } else {
+    dmnsn_free(branch->location);
+  }
+}
+
+/** Destructor function for thread-specific profile data. */
+static void
+dmnsn_delete_thread_profile(void *ptr)
+{
+  dmnsn_dictionary *thread_profile = ptr;
+
+  if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't lock mutex.");
+  }
+  dmnsn_dictionary_apply(thread_profile, &dmnsn_profile_globalize);
+  if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't unlock mutex.");
+  }
+
+  dmnsn_delete_dictionary(thread_profile);
+}
+
+/** Initialize the thread-specific pointer. */
+static void
+dmnsn_initialize_thread_profile(void)
+{
+  if (pthread_key_create(&dmnsn_thread_profile,
+                         &dmnsn_delete_thread_profile)
+      != 0)
+  {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "pthread_key_create() failed.");
+  }
+
+  if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't lock mutex.");
+  }
+  dmnsn_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch));
+  if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't unlock mutex.");
+  }
+}
+
+/** Get the thread-specific profile data. */
+static dmnsn_dictionary *
+dmnsn_get_thread_profile(void)
+{
+  if (pthread_once(&dmnsn_thread_profile_once,
+                   &dmnsn_initialize_thread_profile)
+      != 0)
+  {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "pthread_once() failed.");
+  }
+
+  return pthread_getspecific(dmnsn_thread_profile);
+}
+
+/** Set the thread-specific profile data. */
+static void
+dmnsn_set_thread_profile(dmnsn_dictionary *thread_profile)
+{
+  if (pthread_setspecific(dmnsn_thread_profile, thread_profile) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "pthread_setspecific() failed.");
+  }
+}
+
 bool
 dmnsn_expect(bool result, bool expected, const char *func, const char *file,
              unsigned int line)
 {
   int size = snprintf(NULL, 0, "%s:%s:%u", file, func, line) + 1;
   if (size < 1) {
-    dmnsn_error(DMNSN_SEVERITY_MEDIUM, "sprintf() failed.");
-  } else {
-    char key[size];
-    if (snprintf(key, size, "%s:%s:%u", file, func, line) > 0) {
-      if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
-        dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't lock mutex.");
-      }
-
-      if (!dmnsn_profile_data) {
-        dmnsn_profile_data = dmnsn_new_dictionary(sizeof(dmnsn_profile_info));
-      }
-      dmnsn_profile_info *info = dmnsn_dictionary_at(dmnsn_profile_data, key);
-      if (info) {
-        ++info->branches;
-        if (result == expected) {
-          ++info->predicted;
-        }
-      } else {
-        dmnsn_profile_info info = {
-          .location  = dmnsn_strdup(key),
-          .predicted = (result == expected) ? 1 : 0,
-          .branches  = 1
-        };
-        dmnsn_dictionary_insert(dmnsn_profile_data, key, &info);
-      }
-
-      if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
-        dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't lock mutex.");
-      }
-    } else {
-      dmnsn_error(DMNSN_SEVERITY_MEDIUM, "sprintf() failed.");
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "sprintf() failed.");
+  }
+
+  char key[size];
+  if (snprintf(key, size, "%s:%s:%u", file, func, line) < 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "sprintf() failed.");
+  }
+
+  dmnsn_dictionary *thread_profile = dmnsn_get_thread_profile();
+  if (!thread_profile) {
+    thread_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch));
+    dmnsn_set_thread_profile(thread_profile);
+  }
+
+  dmnsn_branch *branch = dmnsn_dictionary_at(thread_profile, key);
+  if (branch) {
+    ++branch->branches;
+    if (result == expected) {
+      ++branch->predicted;
     }
+  } else {
+    dmnsn_branch branch = {
+      .location  = dmnsn_strdup(key),
+      .predicted = (result == expected) ? 1 : 0,
+      .branches  = 1
+    };
+    dmnsn_dictionary_insert(thread_profile, key, &branch);
   }
 
   return result;
@@ -85,22 +166,33 @@ dmnsn_expect(bool result, bool expected, const char *func, const char *file,
 static void
 dmnsn_print_bad_prediction(void *ptr)
 {
-  dmnsn_profile_info *info = ptr;
-  double rate = ((double)info->predicted)/info->branches;
-  if (rate < 0.75 || info->branches < 100000) {
+  dmnsn_branch *branch = ptr;
+  double rate = ((double)branch->predicted)/branch->branches;
+  if (rate < 0.75 || branch->branches < 100000) {
     fprintf(stderr,
             "Bad branch prediction: %s: %" PRIu64 "/%" PRIu64 " (%g%%)\n",
-            info->location, info->predicted, info->branches, 100.0*rate);
+            branch->location, branch->predicted, branch->branches, 100.0*rate);
   }
 
-  dmnsn_free(info->location);
+  dmnsn_free(branch->location);
 }
 
 static void __attribute__((destructor))
 dmnsn_print_bad_predictions(void)
 {
-  if (dmnsn_profile_data) {
-    dmnsn_dictionary_apply(dmnsn_profile_data, &dmnsn_print_bad_prediction);
-    dmnsn_delete_dictionary(dmnsn_profile_data);
+  dmnsn_dictionary *thread_profile = dmnsn_get_thread_profile();
+  if (thread_profile) {
+    dmnsn_delete_thread_profile(thread_profile);
+    dmnsn_set_thread_profile(NULL);
+  }
+
+  if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't lock mutex.");
+  }
+  dmnsn_dictionary_apply(dmnsn_profile, &dmnsn_print_bad_prediction);
+  dmnsn_delete_dictionary(dmnsn_profile);
+  dmnsn_profile = NULL;
+  if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
+    dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't unlock mutex.");
   }
 }
-- 
cgit v1.2.3