Index: init/perf_log.c |
diff --git a/init/perf_log.c b/init/perf_log.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0c9a33f1e3e3900168b5c158cc04c227cd0d0f95 |
--- /dev/null |
+++ b/init/perf_log.c |
@@ -0,0 +1,266 @@ |
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifdef HAVE_CONFIG_H |
+# include <config.h> |
+#endif /* HAVE_CONFIG_H */ |
+ |
+ |
+#include <sys/types.h> |
+ |
+#include <stdio.h> |
+#include <string.h> |
+ |
+#include <nih/alloc.h> |
+#include <nih/error.h> |
+#include <nih/file.h> |
+#include <nih/list.h> |
+#include <nih/logging.h> |
+#include <nih/string.h> |
+ |
+#include "perf_log.h" |
+ |
+static NihList *message_list; |
+static const char *perf_log_file; |
+static const char *perf_uptime_file; |
+static const char *perf_diskstats_file; |
+ |
+/** |
+ * load_special_file_contents: |
+ * @file: file to load |
+ * |
+ * Loads an ASCII text file and returns as a string. File must be less than |
+ * MAX_FILE_SIZE. Do not use nih_file_read as it requires reading a |
+ * normal file whose size is correct (unlike /proc and /sys files). |
+ * |
+ * Returns: none |
+ **/ |
+static char * |
+load_special_file_contents (void *parent, |
+ const char *file) |
+{ |
+ const int MAX_FILE_SIZE = 512; |
+ size_t len = 0; |
+ char *contents; |
+ FILE *fp = NULL; |
+ |
+ if (! file) { |
+ return NULL; |
+ } |
+ fp = fopen (file, "r"); |
+ if (! fp) { |
+ return NULL; |
+ } |
+ contents = NIH_MUST (nih_alloc (parent, MAX_FILE_SIZE + 1)); |
+ len = fread (contents, 1, MAX_FILE_SIZE, fp); |
+ fclose (fp); |
+ if (len < 0) { |
+ nih_free(contents); |
+ return NULL; |
+ } |
+ contents[len] = '\0'; |
+ return contents; |
+} |
+ |
+/** |
+ * get_file_fields: |
+ * @parent: parent context |
+ * @file: file to load and parse |
+ * @delimiters: NULL terminated list of characters that delimits fields |
+ * @fields: point to integer that is set to the number of fields found |
+ * |
+ * Loads @file and returns an array of fields delimited by the given |
+ * @delimiters array. Avoid nih_file_read as it requires reading a |
+ * normal file whose size is correct (unlike /proc and /sys files). |
+ * |
+ * Returns: array of delimited fields or NULL on error. |
+ **/ |
+char ** |
+get_file_fields (void *parent, |
+ const char *file, |
+ char *delimiters, |
+ int *fields) |
+{ |
+ int i; |
+ char *result = NULL; |
+ char **array = NULL; |
+ char *contents = NULL; |
+ |
+ nih_assert (fields != NULL); |
+ *fields = 0; |
+ contents = load_special_file_contents (NULL, |
+ file); |
+ if (! contents) { |
+ return NULL; |
+ } |
+ array = nih_str_split (parent, |
+ contents, |
+ delimiters, |
+ TRUE); |
+ nih_free (contents); |
+ if (! array) { |
+ return NULL; |
+ } |
+ i = 0; |
+ while (array[i] != NULL) { |
+ ++i; |
+ } |
+ *fields = i; |
+ return array; |
+} |
+ |
+/** |
+ * perf_log_init: |
+ * |
+ * Initialise the message_list list. |
+ * |
+ * Returns: none |
+ **/ |
+void |
+perf_log_init (void) |
+{ |
+ if (! message_list) { |
+ message_list = NIH_MUST (nih_list_new (NULL)); |
+ } |
+} |
+ |
+/** |
+ * perf_log_flush: |
+ * |
+ * Attempt to write any enqueued perf log messages. |
+ * |
+ * Returns: none |
+ **/ |
+void |
+perf_log_flush (void) |
+{ |
+ FILE *fp = NULL; |
+ |
+ if (perf_log_file) |
+ fp = fopen (perf_log_file, "a"); |
+ if (! fp) |
+ return; |
+ NIH_LIST_FOREACH_SAFE (message_list, iter) { |
+ NihListEntry *entry = (NihListEntry*)iter; |
+ int result; |
+ |
+ result = fputs (entry->str, fp); |
+ if (result < 0) { |
+ /* This is an unexpected error. We retry |
+ * writing the message later. |
+ */ |
+ break; |
+ } |
+ nih_list_remove (iter); |
+ nih_free (iter); |
+ } |
+ |
+ fclose (fp); |
+} |
+ |
+/** |
+ * perf_log_message: |
+ * @format: format string |
+ * |
+ * Log the given formatted message. If the file cannot be written at |
+ * this time, we enqueue the message and try later. We grab |
+ * performance stats now, and those stats are enqueued to write later. |
+ * If the performance stats are not readable at this time, we log "-" |
+ * instead. |
+ * |
+ * Returns: none |
+ **/ |
+void |
+perf_log_message (const char *format, |
+ ...) |
+{ |
+ NihListEntry *new_entry; |
+ va_list args; |
+ int uptime_fields = 0; |
+ char **uptimes; |
+ int diskstats_fields = 0; |
+ char **diskstats; |
+ char *uptime_busy; |
+ char *sectors_read; |
+ char *message; |
+ |
+ perf_log_init (); |
+ uptimes = get_file_fields (NULL, |
+ perf_uptime_file, |
+ " \n", |
+ &uptime_fields); |
+ diskstats = get_file_fields (NULL, |
+ perf_diskstats_file, |
+ " \n", |
+ &diskstats_fields); |
+ |
+ if (uptime_fields < 1) |
+ uptime_busy = "-"; |
+ else |
+ uptime_busy = uptimes[0]; |
+ if (diskstats_fields < 3) |
+ sectors_read = "-"; |
+ else |
+ sectors_read = diskstats[2]; |
+ |
+ va_start (args, format); |
+ message = NIH_MUST (nih_vsprintf (NULL, |
+ format, |
+ args)); |
+ va_end (args); |
+ |
+ /* Create a log entry and add it to the queue. */ |
+ new_entry = NIH_MUST (nih_list_entry_new (NULL)); |
+ new_entry->str = NIH_MUST (nih_sprintf (new_entry, "%s %s %s", |
+ uptime_busy, sectors_read, |
+ message)); |
+ nih_list_add (message_list, &new_entry->entry); |
+ |
+ nih_free (message); |
+ if (uptimes) |
+ nih_free (uptimes); |
+ if (diskstats) |
+ nih_free (diskstats); |
+ |
+ perf_log_flush (); |
+} |
+ |
+/** |
+ * perf_log_job_state_change: |
+ * @job: job whose state is changing, |
+ * @new_state: new state |
+ * |
+ * Causes the given job's state transition to be logged to the |
+ * performance log. |
+ * |
+ * Returns: none |
+ **/ |
+void |
+perf_log_job_state_change (Job *job, |
+ JobState new_state) |
+{ |
+ perf_log_message ("statechange %s %s\n", |
+ job_name (job), |
+ job_state_name (new_state)); |
+} |
+ |
+/** |
+ * perf_log_set_file: |
+ * @file: file to write to |
+ * |
+ * Sets the performance logging file name and writes to it if possible. |
+ * |
+ * Returns: none |
+ **/ |
+void |
+perf_log_set_files (const char *uptime_file, |
+ const char *diskstats_file, |
+ const char *log_file) |
+{ |
+ perf_log_init (); |
+ perf_log_file = log_file; |
+ perf_uptime_file = uptime_file; |
+ perf_diskstats_file = diskstats_file; |
+ perf_log_flush (); |
+} |