else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
}
-#define dictionary_name_value_index_add_nolock(dict, nv) do { (dict)->inserts++; avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
-#define dictionary_name_value_index_del_nolock(dict, nv) do { (dict)->deletes++; avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
+#define dictionary_name_value_index_add_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
+#define dictionary_name_value_index_del_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
NAME_VALUE tmp;
tmp.hash = (hash)?hash:simple_hash(name);
tmp.name = (char *)name;
- dict->searches++;
+ NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
}
// index it
dictionary_name_value_index_add_nolock(dict, nv);
- dict->entries++;
+ NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
return nv;
}
dictionary_name_value_index_del_nolock(dict, nv);
- dict->entries--;
+ NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
uint8_t flags;
+#ifdef NETDATA_DICTIONARY_WITH_STATISTICS
unsigned long long inserts;
unsigned long long deletes;
unsigned long long searches;
unsigned long long entries;
+#endif /* NETDATA_DICTIONARY_WITH_STATISTICS */
pthread_rwlock_t rwlock;
} DICTIONARY;
+#ifdef NETDATA_DICTIONARY_WITH_STATISTICS
+#define NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict) (dict)->inserts++
+#define NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict) (dict)->deletes++
+#define NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict) (dict)->searches++
+#define NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict) (dict)->entries++
+#define NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict) (dict)->entries--
+#else /* NETDATA_DICTIONARY_WITH_STATISTICS */
+#define NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict)
+#define NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict)
+#define NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict)
+#define NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict)
+#define NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict)
+#endif /* NETDATA_DICTIONARY_WITH_STATISTICS */
+
#define DICTIONARY_FLAG_DEFAULT 0x00000000
#define DICTIONARY_FLAG_SINGLE_THREADED 0x00000001
#define DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE 0x00000002
#include "global_statistics.h"
-struct global_statistics global_statistics = { 0ULL, 0ULL, 0ULL, 0ULL };
+struct global_statistics global_statistics = { 0, 0ULL, 0ULL, 0ULL, 0ULL};
pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER;
// global statistics
struct global_statistics {
- unsigned long long connected_clients;
- unsigned long long web_requests;
- unsigned long long bytes_received;
- unsigned long long bytes_sent;
-
+ unsigned long volatile connected_clients;
+ unsigned long long volatile web_requests;
+ unsigned long long volatile web_usec;
+ unsigned long long volatile bytes_received;
+ unsigned long long volatile bytes_sent;
};
extern struct global_statistics global_statistics;
void *proc_main(void *ptr)
{
- if(ptr) { ; }
+ static unsigned long long old_web_requests = 0, old_web_usec = 0;
+ (void)ptr;
info("PROC Plugin thread created with task id %d", gettid());
unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
unsigned long long sunow;
- RRDSET *stcpu = NULL, *stcpu_thread = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL;
+ RRDSET *stcpu = NULL, *stcpu_thread = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL, *stduration = NULL;
for(;1;) {
if(unlikely(netdata_exit)) break;
// ----------------------------------------------------------------
+ if(!stduration) stduration = rrdset_find("netdata.response_time");
+ if(!stduration) {
+ stduration = rrdset_create("netdata", "response_time", NULL, "netdata", NULL, "NetData Average API Response Time", "ms/request", 130400, rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(stduration, "response_time", "response time", 1, 1000, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(stduration);
+
+ unsigned long long gweb_usec = global_statistics.web_usec;
+ unsigned long long gweb_requests = global_statistics.web_requests;
+
+ unsigned long long web_usec = gweb_usec - old_web_usec;
+ unsigned long long web_requests = gweb_requests - old_web_requests;
+
+ old_web_usec = gweb_usec;
+ old_web_requests = gweb_requests;
+
+ if(!web_requests) web_requests = 1;
+
+ rrddim_set(stduration, "response_time", web_usec / web_requests);
+ rrdset_done(stduration);
+
+ // ----------------------------------------------------------------
+
registry_statistics();
}
}
/* Returns a url-decoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_decode(char *str) {
- char *pstr = str,
- *buf = malloc(strlen(str) + 1),
- *pbuf = buf;
+ size_t size = strlen(str) + 1;
+ char *buf = malloc(size);
if(!buf)
- fatal("Cannot allocate memory.");
+ fatal("Cannot allocate %zu bytes of memory.", size);
+
+ return url_decode_r(buf, str, size);
+}
+
+char *url_decode_r(char *to, char *url, size_t size) {
+ char *s = url, // source
+ *d = to, // destination
+ *e = &to[size - 1]; // destination end
- while (*pstr) {
- if (*pstr == '%') {
- if (pstr[1] && pstr[2]) {
- *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
- pstr += 2;
+ while(*s && d < e) {
+ if(unlikely(*s == '%')) {
+ if(likely(s[1] && s[2])) {
+ *d++ = from_hex(s[1]) << 4 | from_hex(s[2]);
+ s += 2;
}
}
- else if (*pstr == '+')
- *pbuf++ = ' ';
+ else if(unlikely(*s == '+'))
+ *d++ = ' ';
else
- *pbuf++ = *pstr;
+ *d++ = *s;
- pstr++;
+ s++;
}
- *pbuf = '\0';
+ *d = '\0';
- return buf;
+ return to;
}
-
/* IMPORTANT: be sure to free() the returned string after use */
extern char *url_decode(char *str);
+extern char *url_decode_r(char *to, char *url, size_t size);
+
#endif /* NETDATA_URL_H */
struct timeval tv;
gettimeofday(&tv, NULL);
+ debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
+
+ if(w->stats_received_bytes || w->stats_sent_bytes) {
+ global_statistics_lock();
+ global_statistics.web_requests++;
+ global_statistics.web_usec += usecdiff(&tv, &w->tv_in);
+ global_statistics.bytes_received += w->stats_received_bytes;
+ global_statistics.bytes_sent += w->stats_sent_bytes;
+ global_statistics_unlock();
+ }
+ w->stats_received_bytes = 0;
+ w->stats_sent_bytes = 0;
+
long sent = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
#ifdef NETDATA_WITH_ZLIB
w->last_url
);
- debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
-
if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
debug(D_WEB_CLIENT, "%llu: Closing filecopy input file.", w->id);
close(w->ifd);
w->mode = WEB_CLIENT_MODE_NORMAL;
w->enable_gzip = 0;
w->keepalive = 0;
- if(w->decoded_url) {
- free(w->decoded_url);
- w->decoded_url = NULL;
- }
+ w->decoded_url[0] = '\0';
buffer_reset(w->response.header_output);
buffer_reset(w->response.header);
// http_request_validate()
// returns:
// = 0 : all good, process the request
-// > 0 : request is complete, but is not supported
+// > 0 : request is not supported
// < 0 : request is incomplete - wait for more data
static inline int http_request_validate(struct web_client *w) {
// find the SPACE + "HTTP/"
while(*s) {
- // find the space
+ // find the next space
while (*s && *s != ' ') s++;
// is it SPACE + "HTTP/" ?
}
// incomplete requests
- if(!*s) {
+ if(unlikely(!*s)) {
w->wait_receive = 1;
return -2;
}
// we have the end of encoded_url - remember it
char *ue = s;
+ // make sure we have complete request
+ // complete requests contain: \r\n\r\n
while(*s) {
// find a line feed
- while (*s && *s != '\r') s++;
+ while(*s && *s++ != '\r');
// did we reach the end?
if(unlikely(!*s)) break;
// is it \r\n ?
- if (likely(s[1] == '\n')) {
+ if(likely(*s++ == '\n')) {
// is it again \r\n ? (header end)
- if(unlikely(s[2] == '\r' && s[3] == '\n')) {
+ if(unlikely(*s == '\r' && s[1] == '\n')) {
// a valid complete HTTP request found
*ue = '\0';
- w->decoded_url = url_decode(encoded_url);
+ url_decode_r(w->decoded_url, encoded_url, URL_MAX + 1);
*ue = ' ';
+
+ // copy the URL - we are going to overwrite parts of it
+ // FIXME -- we should avoid it
+ strncpyz(w->last_url, w->decoded_url, URL_MAX);
w->wait_receive = 0;
return 0;
}
// another header line
- s = http_header_parse(w, &s[2]);
+ s = http_header_parse(w, s);
}
- else s++;
}
// incomplete request
else { // what_to_do == 0
gettimeofday(&w->tv_in, NULL);
- global_statistics_lock();
- global_statistics.web_requests++;
- global_statistics_unlock();
-
- // copy the URL - we are going to overwrite parts of it
- // FIXME -- we should avoid it
- strncpyz(w->last_url, w->decoded_url, URL_MAX);
-
if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
code = 200;
w->response.data->contenttype = CT_TEXT_PLAIN;
, w->id
, buffer_strlen(w->response.header_output)
, bytes);
- else {
- global_statistics_lock();
- global_statistics.bytes_sent += bytes;
- global_statistics_unlock();
- }
+ else
+ w->stats_sent_bytes += bytes;
// enable TCP_NODELAY, to send all data immediately at the next send()
flag = 1;
errno = 0;
break;
}
-
- global_statistics_lock();
- global_statistics.bytes_sent += bytes;
- global_statistics_unlock();
+ else
+ w->stats_sent_bytes += bytes;
}
if(w->wait_receive && FD_ISSET(w->ifd, &ifds)) {
errno = 0;
break;
}
+ else
+ w->stats_received_bytes += bytes;
if(w->mode == WEB_CLIENT_MODE_NORMAL) {
debug(D_WEB_CLIENT, "%llu: Attempting to process received data (%ld bytes).", w->id, bytes);
web_client_process(w);
}
- global_statistics_lock();
- global_statistics.bytes_received += bytes;
- global_statistics_unlock();
}
}
char client_ip[NI_MAXHOST+1];
char client_port[NI_MAXSERV+1];
- char last_url[URL_MAX+1];
+ char decoded_url[URL_MAX + 1]; // we decode the URL in this buffer
+ char last_url[URL_MAX+1]; // we keep a copy of the decoded URL here
struct timeval tv_in, tv_ready;
int mode;
int keepalive;
int enable_gzip;
- char *decoded_url;
struct sockaddr_storage clientaddr;
int wait_receive;
int wait_send;
+ unsigned long stats_received_bytes;
+ unsigned long stats_sent_bytes;
+
struct web_client *prev;
struct web_client *next;
};