Beispiel #1
0
static void stats_add_session(struct mail_user *user)
{
	struct stats_user *suser = STATS_USER_CONTEXT(user);
	struct stats *new_stats, *diff_stats;
	const char *error;

	new_stats = stats_alloc(pool_datastack_create());
	diff_stats = stats_alloc(pool_datastack_create());

	mail_user_stats_fill(user, new_stats);
	/* we'll count new_stats-pre_io_stats and add the changes to
	   session_stats. the new_stats can't be directly copied to
	   session_stats because there are some fields that don't start from
	   zero, like clock_time. (actually with stats_global_user code we're
	   requiring that clock_time is the only such field..) */
	if (!stats_diff(suser->pre_io_stats, new_stats, diff_stats, &error))
		i_error("stats: session stats shrank: %s", error);
	stats_add(suser->session_stats, diff_stats);
	/* copying is only needed if stats_global_user=NULL */
	stats_copy(suser->pre_io_stats, new_stats);
}
Beispiel #2
0
int mail_session_update_parse(const char *const *args, const char **error_r)
{
	struct mail_session *session;
	struct stats *new_stats, *diff_stats;
	buffer_t *buf;
	const char *error;

	/* <session id> <stats> */
	if (mail_session_get(args[0], &session, error_r) < 0)
		return -1;

	buf = buffer_create_dynamic(pool_datastack_create(), 256);
	if (args[1] == NULL ||
	    base64_decode(args[1], strlen(args[1]), NULL, buf) < 0) {
		*error_r = t_strdup_printf("UPDATE-SESSION %s %s %s: Invalid base64 input",
					   session->user->name,
					   session->service, session->id);
		return -1;
	}

	new_stats = stats_alloc(pool_datastack_create());
	diff_stats = stats_alloc(pool_datastack_create());

	if (!stats_import(buf->data, buf->used, session->stats, new_stats, &error)) {
		*error_r = t_strdup_printf("UPDATE-SESSION %s %s %s: %s",
					   session->user->name,
					   session->service, session->id, error);
		return -1;
	}

	if (!stats_diff(session->stats, new_stats, diff_stats, &error)) {
		*error_r = t_strdup_printf("UPDATE-SESSION %s %s %s: stats shrank: %s",
					   session->user->name,
					   session->service, session->id, error);
		i_panic("%s", *error_r);
		return -1;
	}
	mail_session_refresh(session, diff_stats);
	return 0;
}
Beispiel #3
0
static void stats_command_pre(struct client_command_context *cmd)
{
	struct stats_user *suser = STATS_USER_CONTEXT(cmd->client->user);
	struct stats_client_command *scmd;
	static unsigned int stats_cmd_id_counter = 0;

	if (suser == NULL || !suser->track_commands)
		return;

	if (strcasecmp(cmd->name, "IDLE") == 0) {
		/* IDLE can run forever and waste stats process's memory while
		   waiting for it to timeout. don't send them. */
		return;
	}

	scmd = IMAP_STATS_IMAP_CONTEXT(cmd);
	if (scmd == NULL) {
		scmd = p_new(cmd->pool, struct stats_client_command, 1);
		scmd->id = ++stats_cmd_id_counter;
		scmd->stats = stats_alloc(cmd->pool);
		scmd->pre_stats = stats_alloc(cmd->pool);
		MODULE_CONTEXT_SET(cmd, imap_stats_imap_module, scmd);
	}
Beispiel #4
0
static void stats_user_created(struct mail_user *user)
{
	struct ioloop_context *ioloop_ctx =
		io_loop_get_current_context(current_ioloop);
	struct stats_user *suser;
	struct mail_user_vfuncs *v = user->vlast;
	const char *path, *str, *error;
	unsigned int refresh_secs;

	if (ioloop_ctx == NULL) {
		/* we're probably running some test program, or at least
		   mail-storage-service wasn't used to create this user.
		   disable stats tracking. */
		return;
	}
	if (user->autocreated) {
		/* lda / shared user. we're not tracking this one. */
		return;
	}

	/* get refresh time */
	str = mail_user_plugin_getenv(user, "stats_refresh");
	if (str == NULL)
		return;
	if (settings_get_time(str, &refresh_secs, &error) < 0) {
		i_error("stats: Invalid stats_refresh setting: %s", error);
		return;
	}
	if (refresh_secs == 0)
		return;
	if (refresh_secs > SESSION_STATS_FORCE_REFRESH_SECS) {
		i_warning("stats: stats_refresh too large, changing to %u",
			  SESSION_STATS_FORCE_REFRESH_SECS);
		refresh_secs = SESSION_STATS_FORCE_REFRESH_SECS;
	}

	if (global_stats_conn == NULL) {
		path = t_strconcat(user->set->base_dir,
				   "/"MAIL_STATS_SOCKET_NAME, NULL);
		global_stats_conn = stats_connection_create(path);
	}
	stats_connection_ref(global_stats_conn);

	if (stats_user_count == 0) {
		/* first user connection */
		stats_global_user = user;
	} else if (stats_user_count == 1) {
		/* second user connection. we'll need to start doing
		   per-io callback tracking now. (we might have been doing it
		   also previously but just temporarily quickly dropped to
		   having 1 user, in which case stats_global_user=NULL) */
		if (stats_global_user != NULL) {
			stats_add_session(stats_global_user);
			stats_global_user = NULL;
		}
	}
	stats_user_count++;

	suser = p_new(user->pool, struct stats_user, 1);
	suser->module_ctx.super = *v;
	user->vlast = &suser->module_ctx.super;
	v->deinit = stats_user_deinit;
	v->stats_fill = stats_user_stats_fill;

	suser->refresh_secs = refresh_secs;
	str = mail_user_plugin_getenv(user, "stats_track_cmds");
	if (str != NULL && strcmp(str, "yes") == 0)
		suser->track_commands = TRUE;

	suser->stats_conn = global_stats_conn;
	if (user->session_id != NULL && user->session_id[0] != '\0')
		suser->stats_session_id = user->session_id;
	else {
		guid_128_t guid;

		guid_128_generate(guid);
		suser->stats_session_id =
			p_strdup(user->pool, guid_128_to_string(guid));
	}
	suser->last_session_update = time(NULL);
	user->stats_enabled = TRUE;

	suser->ioloop_ctx = ioloop_ctx;
	io_loop_context_add_callbacks(ioloop_ctx,
				      stats_io_activate,
				      stats_io_deactivate, user);

	suser->pre_io_stats = stats_alloc(user->pool);
	suser->session_stats = stats_alloc(user->pool);
	suser->last_sent_session_stats = stats_alloc(user->pool);

	MODULE_CONTEXT_SET(user, stats_user_module, suser);
	mail_stats_connection_connect(suser->stats_conn, user);
	suser->to_stats_timeout =
		timeout_add(suser->refresh_secs*1000,
			    session_stats_refresh_timeout, user);
	/* fill the initial values. this is necessary for the process-global
	   values (e.g. getrusage()) if the process is reused for multiple
	   users. otherwise the next user will start with the previous one's
	   last values. */
	mail_user_stats_fill(user, suser->pre_io_stats);
}
Beispiel #5
0
int main(int argc, char **argv) {
    struct addrinfo *addrs, *addr;
    char *host = "127.0.0.1";
    char *port = "1337";
    int rc;
    // for checking that the server's up
    char poke[SHA_LENGTH * 2];
    tinymt64_t rando;

    if (parse_args(&cfg, &host, &port, argc, argv)) {
        usage();
        exit(1);
    }

    struct addrinfo hints = {
        .ai_family   = AF_UNSPEC,
        .ai_socktype = SOCK_STREAM
    };

    if ((rc = getaddrinfo(host, port, &hints, &addrs)) != 0) {
        const char *msg = gai_strerror(rc);
        fprintf(stderr, "unable to resolve %s:%s: %s\n", host, port, msg);
        exit(1);
    }

    for (addr = addrs; addr != NULL; addr = addr->ai_next) {
        int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (fd == -1) continue;
        rc = connect(fd, addr->ai_addr, addr->ai_addrlen);
        tinymt64_init(&rando, time_us());
        random_hash(&rando, poke);
        if (rc == 0 && write(fd, poke, SHA_LENGTH * 2) == SHA_LENGTH * 2) {
            read(fd, poke, SHA_LENGTH * 2);
            close(fd);
            break;
        }
        close(fd);
    }

    if (addr == NULL) {
        char *msg = strerror(errno);
        fprintf(stderr, "unable to connect to %s:%s: %s\n", host, port, msg);
        exit(1);
    }

    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT,  SIG_IGN);
    cfg.addr = *addr;

    pthread_mutex_init(&statistics.mutex, NULL);
    statistics.latency  = stats_alloc(SAMPLES);
    statistics.requests = stats_alloc(SAMPLES);

    thread *threads = zcalloc(cfg.threads * sizeof(thread));
    uint64_t connections = cfg.connections / cfg.threads;
    uint64_t stop_at     = time_us() + (cfg.duration * 1000000);

    for (uint64_t i = 0; i < cfg.threads; i++) {
        thread *t = &threads[i];
        t->connections = connections;
        t->stop_at     = stop_at;

        if (pthread_create(&t->thread, NULL, &thread_main, t)) {
            char *msg = strerror(errno);
            fprintf(stderr, "unable to create thread %"PRIu64" %s\n", i, msg);
            exit(2);
        }
    }

    struct sigaction sa = {
        .sa_handler = handler,
        .sa_flags   = 0,
    };
    sigfillset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);

    char *time = format_time_s(cfg.duration);
    printf("Running %s test @ %s:%s\n", time, host, port);
    printf("  %"PRIu64" threads and %"PRIu64" connections\n", cfg.threads, cfg.connections);

    uint64_t start    = time_us();
    uint64_t complete = 0;
    uint64_t bytes    = 0;
    errors errors     = { 0 };

    for (uint64_t i = 0; i < cfg.threads; i++) {
        thread *t = &threads[i];
        pthread_join(t->thread, NULL);

        complete += t->complete;
        bytes    += t->bytes;

        errors.connect   += t->errors.connect;
        errors.handshake += t->errors.handshake;
        errors.read      += t->errors.read;
        errors.validate  += t->errors.validate;
        errors.write     += t->errors.write;
        errors.timeout   += t->errors.timeout;
    }

    uint64_t runtime_us = time_us() - start;
    long double runtime_s   = runtime_us / 1000000.0;
    long double req_per_s   = complete   / runtime_s;
    long double bytes_per_s = bytes      / runtime_s;

    print_stats_header();
    print_stats("Latency", statistics.latency, format_time_us);
    print_stats("Req/Sec", statistics.requests, format_metric);
    if (cfg.latency) print_stats_latency(statistics.latency);

    char *runtime_msg = format_time_us(runtime_us);

    printf("  %"PRIu64" requests in %s, %sB read\n", complete, runtime_msg, format_binary(bytes));
    if (errors.connect || errors.read || errors.write || errors.timeout) {
        printf("  Socket errors: connect %d, read %d, write %d, timeout %d\n",
               errors.connect, errors.read, errors.write, errors.timeout);
    }

    if (errors.handshake) {
        printf("  Bad handshakes from server: %d\n", errors.handshake);
    }

    if (errors.validate) {
        printf("  %d proofs failed verification.\n", errors.validate);
    }

    printf("Requests/sec: %9.2Lf\n", req_per_s);
    printf("Transfer/sec: %10sB\n", format_binary(bytes_per_s));

    return 0;
}

void *thread_main(void *arg) {
    thread *thread = arg;

    aeEventLoop *loop = aeCreateEventLoop(10 + cfg.connections * 3);
    thread->cs   = zmalloc(thread->connections * sizeof(connection));
    thread->loop = loop;
    tinymt64_init(&thread->rand, time_us());
    thread->latency = stats_alloc(100000);

    connection *c = thread->cs;

    for (uint64_t i = 0; i < thread->connections; i++, c++) {
        c->thread = thread;
        random_hash(&thread->rand, c->hash);
        connect_socket(thread, c);
    }

    aeCreateTimeEvent(loop, CALIBRATE_DELAY_MS, calibrate, thread, NULL);
    aeCreateTimeEvent(loop, TIMEOUT_INTERVAL_MS, check_timeouts, thread, NULL);

    thread->start = time_us();
    aeMain(loop);

    aeDeleteEventLoop(loop);
    zfree(thread->cs);

    uint64_t max = thread->latency->max;
    stats_free(thread->latency);

    pthread_mutex_lock(&statistics.mutex);
    for (uint64_t i = 0; i < thread->missed; i++) {
        stats_record(statistics.latency, max);
    }
    pthread_mutex_unlock(&statistics.mutex);

    return NULL;
}
Beispiel #6
0
int mail_command_update_parse(const char *const *args, const char **error_r)
{
	struct mail_session *session;
	struct mail_command *cmd;
	struct stats *new_stats, *diff_stats;
	buffer_t *buf;
	const char *error;
	unsigned int i, cmd_id;
	bool done = FALSE, continued = FALSE;

	/* <session guid> <cmd id> [d] <name> <args> <stats>
	   <session guid> <cmd id> c[d] <stats> */
	if (str_array_length(args) < 3) {
		*error_r = "UPDATE-CMD: Too few parameters";
		return -1;
	}
	if (mail_session_get(args[0], &session, error_r) < 0)
		return -1;

	if (str_to_uint(args[1], &cmd_id) < 0 || cmd_id == 0) {
		*error_r = "UPDATE-CMD: Invalid command id";
		return -1;
	}
	for (i = 0; args[2][i] != '\0'; i++) {
		switch (args[2][i]) {
		case 'd':
			done = TRUE;
			break;
		case 'c':
			continued = TRUE;
			break;
		default:
			*error_r = "UPDATE-CMD: Invalid flags parameter";
			return -1;
		}
	}

	cmd = mail_command_find(session, cmd_id);
	if (!continued) {
		/* new command */
		if (cmd != NULL) {
			*error_r = "UPDATE-CMD: Duplicate new command id";
			return -1;
		}
		if (str_array_length(args) < 5) {
			*error_r = "UPDATE-CMD: Too few parameters";
			return -1;
		}
		cmd = mail_command_add(session, args[3], args[4]);
		cmd->id = cmd_id;

		session->highest_cmd_id =
			I_MAX(session->highest_cmd_id, cmd_id);
		session->num_cmds++;
		session->user->num_cmds++;
		session->user->domain->num_cmds++;
		if (session->ip != NULL)
			session->ip->num_cmds++;
		mail_global_stats.num_cmds++;
		args += 5;
	} else {
		if (cmd == NULL) {
			/* already expired command, ignore */
			i_warning("UPDATE-CMD: Already expired");
			return 0;
		}
		args += 3;
		cmd->last_update = ioloop_timeval;
	}
	buf = t_buffer_create(256);
	if (args[0] == NULL ||
	    base64_decode(args[0], strlen(args[0]), NULL, buf) < 0) {
		*error_r = t_strdup_printf("UPDATE-CMD: Invalid base64 input");
		return -1;
	}

	new_stats = stats_alloc(pool_datastack_create());
	diff_stats = stats_alloc(pool_datastack_create());

	if (!stats_import(buf->data, buf->used, cmd->stats, new_stats, &error)) {
		*error_r = t_strdup_printf("UPDATE-CMD: %s", error);
		return -1;
	}

	if (!stats_diff(cmd->stats, new_stats, diff_stats, &error)) {
		*error_r = t_strdup_printf("UPDATE-CMD: stats shrank: %s", error);
		return -1;
	}
	stats_add(cmd->stats, diff_stats);

	if (done) {
		cmd->id = 0;
		mail_command_unref(&cmd);
	}
	mail_session_refresh(session, NULL);
	return 0;
}
Beispiel #7
0
int
main (int argc, char **argv)
{
  List nflist;

  int summary = FALSE;
  int error_occurred = FALSE;
  struct stats *total_stats = stats_alloc ();
  int processed = 0;

  int opt;
  int option_index = 0;
  extern char *optarg;
  extern int optind, opterr, optopt;

  struct option long_options[] =
    {
      {"debug",0,0,'D'},
      {"summary",0,0,'s'},
      {"help",0,0,'h'},
      {"version",0,0,0},
      {0,0,0,0}
    };

#ifdef __GLIBC__
  program_name = program_invocation_short_name;
#else
  program_name = base_name (argv[0]);
#endif

  /* Initialize i18n. */

#ifdef HAVE_SETLOCALE
  setlocale (LC_ALL, "");
#endif

#if ENABLE_NLS
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
#endif

  setup ();

  while ((opt = getopt_long (argc, argv, "sh",
                             long_options, &option_index)) != -1)
    {
      switch (opt)
        {
        case 0:
          {
            printf_version_string (N_("nfstats"));

            teardown ();

            if (fclose (stdout) == EOF)
              error (EXIT_FAILURE, errno, _("error writing output"));
            exit (EXIT_SUCCESS);
          }

        case 'D':
          debug = TRUE;
          break;

        case 's':
          summary = TRUE;
          break;

        case 'h':
          printf (_("Usage: %s [OPTION]... NOTESFILE...\n"
                    "Display usage statistics for NOTESFILE(s), including a total.\n\n"),
                  program_name);

          printf (_("  -s, --summary   Print only the total for all listed notesfiles\n"
                    "      --debug     Display debugging messages\n\n"
                    "  -h, --help      Display this help and exit\n"
                    "      --version   Display version information and exit\n\n"));

          printf (_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);

          teardown ();

          if (fclose (stdout) == EOF)
            error (EXIT_FAILURE, errno, _("error writing output"));
          exit (EXIT_SUCCESS);

        case '?':
          fprintf (stderr, _("Try '%s --help' for more information.\n"),
                   program_name);

          teardown ();

          exit (EXIT_FAILURE);
        }
    }

  if (optind == argc)
    {
      fprintf (stderr, _("%s: too few arguments\n"), program_name);
      fprintf (stderr, _("Try '%s --help' for more information.\n"),
               program_name);

      teardown ();

      exit (EXIT_FAILURE);
    }

  list_init (&nflist,
             (void * (*) (void)) nfref_alloc,
             (void (*) (void *)) nfref_free,
             NULL);

  while (optind < argc)
    parse_nf (argv[optind++], &nflist);

  {
    ListNode *node = list_head (&nflist);
    struct stats *stats = stats_alloc ();

    while (node != NULL && !error_occurred)
      {
        newts_nfref *ref = (newts_nfref *) list_data (node);
        int result = get_stats (ref, stats);

        if (result != NEWTS_NO_ERROR)
          {
            fprintf (stderr, _("%s: error getting stats for '%s'\n"),
                     program_name, nfref_pretty_name (ref));
            error_occurred = TRUE;
            break;
          }

        if (!summary)
          {
            if (processed) printf ("\n");
            printf (_("Usage statistics for %s\n"),
                    nfref_pretty_name (ref));
            printf (_("                         NOTES   RESPS  TOTALS\n"));
            printf (_("Local Reads:           %7u %7u %7u\n"),
                    stats->notes_read, stats->resps_read,
                    (stats->notes_read + stats->resps_read));
            printf (_("Local Writes:          %7u %7u %7u\n"),
                    (stats->notes_written - stats->notes_received),
                    (stats->resps_written - stats->resps_received),
                    (stats->notes_written + stats->resps_written -
                     stats->notes_received - stats->resps_received));
            printf (_("Entries into Notesfile:  %u\n"), stats->entries);
            printf (_("Total Time in Notesfile: %.2f minutes\n"),
                    ((float) stats->total_time / 60.0));
            if (stats->entries)
              printf (_("Average Time/Entry:      %.2f minutes\n"),
                      (((float) stats->total_time / 60.0) /
                       (float) stats->entries));
          }

        stats_accumulate (stats, total_stats);

        processed++;

        node = list_next (node);
      }

    stats_free (stats);
  }

  if (processed && (summary || processed != 1) && !error_occurred)
    {
      if (!summary) printf ("\n");
      printf (_("Total for all requested notesfiles\n"));
      printf (_("                          NOTES   RESPS  TOTALS\n"));
      printf (_("Local Reads:            %7u %7u %7u\n"),
              total_stats->notes_read, total_stats->resps_read,
              (total_stats->notes_read + total_stats->resps_read));
      printf (_("Local Writes:           %7u %7u %7u\n"),
              (total_stats->notes_written - total_stats->notes_received),
              (total_stats->resps_written - total_stats->resps_received),
              (total_stats->notes_written + total_stats->resps_written -
               total_stats->notes_received - total_stats->resps_received));
      printf (_("Entries into Notesfiles:  %u\n"), total_stats->entries);
      printf (_("Total Time in Notesfiles: %.2f minutes\n"),
              ((float) total_stats->total_time / 60.0));
      if (total_stats->entries)
        printf (_("Average Time/Entry:       %.2f minutes\n"),
                (((float) total_stats->total_time / 60.0)
                 / (float) total_stats->entries));
    }

  stats_free (total_stats);

  list_destroy (&nflist);
  teardown ();

  if (fclose (stdout) == EOF)
    error (EXIT_FAILURE, errno, _("error writing output"));

  exit (error_occurred ? EXIT_FAILURE : EXIT_SUCCESS);
}