| 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 ();
|
| +}
|
|
|