Merge pull request #2519 from cesanta/prof

Add profiling capability
This commit is contained in:
Sergey Lyubka authored and GitHub committed 2023-12-08 18:43:14 +00:00
commit ed2d4c1c2b
14 files changed
+239 -23

No files matched your search

+7
View File
@@ -22,8 +22,15 @@
#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
#endif
#define strerror(x) zsock_gai_strerror(x)
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 0
#endif
#ifndef F_SETFD
#define F_SETFD 0
#endif
#define MG_ENABLE_SSI 0
int rand(void);
+4
View File
@@ -153,3 +153,7 @@
#define MG_EPOLL_ADD(c)
#define MG_EPOLL_MOD(c, wr)
#endif
#ifndef MG_ENABLE_PROFILE
#define MG_ENABLE_PROFILE 0
#endif
+12
View File
@@ -2,8 +2,20 @@
#include "log.h"
#include "net.h"
#include "printf.h"
#include "profile.h"
void mg_call(struct mg_connection *c, int ev, void *ev_data) {
#if MG_ENABLE_PROFILE
const char *names[] = {
"EV_ERROR", "EV_OPEN", "EV_POLL", "EV_RESOLVE",
"EV_CONNECT", "EV_ACCEPT", "EV_TLS_HS", "EV_READ",
"EV_WRITE", "EV_CLOSE", "EV_HTTP_MSG", "EV_HTTP_CHUNK",
"EV_WS_OPEN", "EV_WS_MSG", "EV_WS_CTL", "EV_MQTT_CMD",
"EV_MQTT_MSG", "EV_MQTT_OPEN", "EV_SNTP_TIME", "EV_USER"};
if (ev != MG_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) {
MG_PROF_ADD(c, names[ev]);
}
#endif
// Run user-defined handler first, in order to give it an ability
// to intercept processing (e.g. clean input buffer) before the
// protocol handler kicks in
+5
View File
@@ -3,6 +3,7 @@
#include "log.h"
#include "net.h"
#include "printf.h"
#include "profile.h"
#include "timer.h"
#include "tls.h"
@@ -132,6 +133,7 @@ struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
c->mgr = mgr;
c->send.align = c->recv.align = MG_IO_SIZE;
c->id = ++mgr->nextid;
MG_PROF_INIT(c);
}
return c;
}
@@ -145,6 +147,8 @@ void mg_close_conn(struct mg_connection *c) {
// before we deallocate received data, see #1331
mg_call(c, MG_EV_CLOSE, NULL);
MG_DEBUG(("%lu %ld closed", c->id, c->fd));
MG_PROF_DUMP(c);
MG_PROF_FREE(c);
mg_tls_free(c);
mg_iobuf_free(&c->recv);
@@ -181,6 +185,7 @@ struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
MG_ERROR(("OOM %s", url));
} else if (!mg_open_listener(c, url)) {
MG_ERROR(("Failed: %s, errno %d", url, errno));
MG_PROF_FREE(c);
free(c);
c = NULL;
} else {
+1
View File
@@ -49,6 +49,7 @@ struct mg_connection {
unsigned long id; // Auto-incrementing unique connection ID
struct mg_iobuf recv; // Incoming data
struct mg_iobuf send; // Outgoing data
struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE
mg_event_handler_t fn; // User-specified event handler function
void *fn_data; // User-specified function parameter
mg_event_handler_t pfn; // Protocol-specific handler function
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#include "iobuf.h"
// Macros to record timestamped events that happens with a connection.
// They are saved into a c->prof IO buffer, each event is a name and a 32-bit
// timestamp in milliseconds since connection init time.
//
// Test (run in two separate terminals):
// make -C examples/http-server/ CFLAGS_EXTRA=-DMG_ENABLE_PROFILE=1
// curl localhost:8000
// Output:
// 1ea1f1e7 2 net.c:150:mg_close_conn 3 profile:
// 1ea1f1e8 2 net.c:150:mg_close_conn 1ea1f1e6 init
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_OPEN
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_ACCEPT
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_READ
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_HTTP_MSG
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_WRITE
// 1ea1f1e8 2 net.c:150:mg_close_conn 1 EV_CLOSE
//
// Usage:
// Enable profiling by setting MG_ENABLE_PROFILE=1
// Invoke MG_PROF_ADD(c, "MY_EVENT_1") in the places you'd like to measure
#if MG_ENABLE_PROFILE
struct mg_profitem {
const char *name; // Event name
uint32_t timestamp; // Milliseconds since connection creation (MG_EV_OPEN)
};
#define MG_PROFILE_ALLOC_GRANULARITY 256 // Can save 32 items wih to realloc
// Adding a profile item to the c->prof. Must be as fast as possible.
// Reallocation of the c->prof iobuf is not desirable here, that's why we
// pre-allocate c->prof with MG_PROFILE_ALLOC_GRANULARITY.
// This macro just inits and copies 8 bytes, and calls mg_millis(),
// which should be fast enough.
#define MG_PROF_ADD(c, name_) \
do { \
struct mg_iobuf *io = &c->prof; \
uint32_t inittime = ((struct mg_profitem *) io->buf)->timestamp; \
struct mg_profitem item = {name_, (uint32_t) mg_millis() - inittime}; \
mg_iobuf_add(io, io->len, &item, sizeof(item)); \
} while (0)
// Initialising profile for a new connection. Not time sensitive
#define MG_PROF_INIT(c) \
do { \
struct mg_profitem first = {"init", (uint32_t) mg_millis()}; \
mg_iobuf_init(&(c)->prof, 0, MG_PROFILE_ALLOC_GRANULARITY); \
mg_iobuf_add(&c->prof, c->prof.len, &first, sizeof(first)); \
} while (0)
#define MG_PROF_FREE(c) mg_iobuf_free(&(c)->prof)
// Dumping the profile. Not time sensitive
#define MG_PROF_DUMP(c) \
do { \
struct mg_iobuf *io = &c->prof; \
struct mg_profitem *p = (struct mg_profitem *) io->buf; \
struct mg_profitem *e = &p[io->len / sizeof(*p)]; \
MG_INFO(("%lu profile:", c->id)); \
while (p < e) { \
MG_INFO(("%5lx %s", (unsigned long) p->timestamp, p->name)); \
p++; \
} \
} while (0)
#else
#define MG_PROF_INIT(c)
#define MG_PROF_FREE(c)
#define MG_PROF_ADD(c, name)
#define MG_PROF_DUMP(c)
#endif