Profiler Filter for Mongrel2

So I finally found some time, desire and motivation to hack on Mongrel2. For those of you who don’t know what that is - go to its website and get amazed. I’ve decided to start hacking on Mongrel2 just by creating simple filter for profiling requests. It was done almost as easy as said. In about an hour of reading and coding I had simple proof-of-concept profiler filter for Mongrel2. The code can be found on pastebin, but just in case it gets removed, I’m dumping all of the code here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <filter.h>
#include <dbg.h>

#include <sys/time.h>

typedef struct ProfilerSession {
  struct timeval * profilerTimeStart;
  struct timeval * profilerTimeEnd;
} ProfilerSession;

ProfilerSession *session = NULL;

StateEvent filter_transition(StateEvent state, Connection *conn, tns_value_t *config) {
  if (session == NULL) {
      session = (ProfilerSession *)malloc(sizeof(ProfilerSession));
      session->profilerTimeStart = malloc(sizeof(struct timeval));
      session->profilerTimeEnd = malloc(sizeof(struct timeval));
  }
  
  if (state == REQ_RECV) {
      debug("[Profiler:OPEN] Connection opened. Sampling time with gettimeofday.");
      int rc = gettimeofday(session->profilerTimeStart, NULL);
      check(rc == 0, "Something went REALLY wrong with Profiler filter");
      struct timeval startTime = *(session->profilerTimeStart);
      debug("[Profiler:OPEN] Start of request %ldms", startTime.tv_sec * 10000 + startTime.tv_usec);
  } else if (state == RESP_SENT) {
      gettimeofday(session->profilerTimeEnd, NULL);
      struct timeval endTime = *(session->profilerTimeEnd);
      debug("[Profiler:CLOSE] End of request at %ldms", endTime.tv_sec * 10000 + endTime.tv_usec);

      struct timeval startTime = *(session->profilerTimeStart);
      long int diff = (endTime.tv_sec * 10000 + endTime.tv_usec) - (startTime.tv_sec * 10000 + startTime.tv_usec);
      debug("[Profiler:CLOSE] Request took %ldms.", diff);
  }
  return state;
  
error:
  return CLOSE;
}

StateEvent *filter_init(Server *srv, bstring load_path, int *out_nstates) {
  StateEvent states[] = {REQ_RECV, RESP_SENT};
  *out_nstates = Filter_states_length(states);
  check(*out_nstates == 2, "Wrong states array length.");
  
  debug("[Profiler:filter_init] initted filter with needed states:");
  size_t i;
  for (i = 0; i < *out_nstates; i++) {
      debug("[Profiler:filter_init] state: %d", states[i]);
  }

  return Filter_state_list(states, *out_nstates);

error:
  return NULL;
}

Put this thing into $MONGREL2_SOURCE_DIR/tools/filters/profiler.c file and rebuild the mongrel2 with debug mode turned on

1
$ sudo make dev install

Then put the following code into your mongrel2 configuration file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# define the profiler filter variable
profiler = Filter(
  name="/usr/local/lib/mongrel2/filters/profiler.so",
  settings ={}
)

# add filter to the server
main = Server(
  ...
  filters = [
      profiler
  ]
  ...
)

servers = [main]

Also, here are some references that helped me create this thing:

Comments