1
0
mirror of https://github.com/wg/wrk synced 2025-01-08 23:32:54 +08:00

update ae, zmalloc & http_parser

This commit is contained in:
Will 2017-02-05 14:43:13 +09:00
parent 45e4625353
commit 91655b5520
12 changed files with 691 additions and 204 deletions

View File

@ -1,4 +1,4 @@
wrk - a HTTP benchmarking tool # wrk - a HTTP benchmarking tool
wrk is a modern HTTP benchmarking tool capable of generating significant wrk is a modern HTTP benchmarking tool capable of generating significant
load when run on a single multi-core CPU. It combines a multithreaded load when run on a single multi-core CPU. It combines a multithreaded
@ -6,9 +6,9 @@ wrk - a HTTP benchmarking tool
An optional LuaJIT script can perform HTTP request generation, response An optional LuaJIT script can perform HTTP request generation, response
processing, and custom reporting. Details are available in SCRIPTING and processing, and custom reporting. Details are available in SCRIPTING and
several examples are located in scripts/ several examples are located in [scripts/](scripts/).
Basic Usage ## Basic Usage
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
@ -26,7 +26,25 @@ Basic Usage
Requests/sec: 748868.53 Requests/sec: 748868.53
Transfer/sec: 606.33MB Transfer/sec: 606.33MB
Benchmarking Tips ## Command Line Options
-c, --connections: total number of HTTP connections to keep open with
each thread handling N = connections/threads
-d, --duration: duration of the test, e.g. 2s, 2m, 2h
-t, --threads: total number of threads to use
-s, --script: LuaJIT script, see SCRIPTING
-H, --header: HTTP header to add to request, e.g. "User-Agent: wrk"
--latency: print detailed latency statistics
--timeout: record a timeout if a response is not received within
this amount of time.
## Benchmarking Tips
The machine running wrk must have a sufficient number of ephemeral ports The machine running wrk must have a sufficient number of ephemeral ports
available and closed sockets should be recycled quickly. To handle the available and closed sockets should be recycled quickly. To handle the
@ -38,14 +56,14 @@ Benchmarking Tips
building a new HTTP request, and use of response() will necessarily reduce building a new HTTP request, and use of response() will necessarily reduce
the amount of load that can be generated. the amount of load that can be generated.
Acknowledgements ## Acknowledgements
wrk contains code from a number of open source projects including the wrk contains code from a number of open source projects including the
'ae' event loop from redis, the nginx/joyent/node.js 'http-parser', 'ae' event loop from redis, the nginx/joyent/node.js 'http-parser',
and Mike Pall's LuaJIT. Please consult the NOTICE file for licensing and Mike Pall's LuaJIT. Please consult the NOTICE file for licensing
details. details.
Cryptography Notice ## Cryptography Notice
This distribution includes cryptographic software. The country in This distribution includes cryptographic software. The country in
which you currently reside may have restrictions on the import, which you currently reside may have restrictions on the import,

116
src/ae.c
View File

@ -91,6 +91,36 @@ err:
return NULL; return NULL;
} }
/* Return the current set size. */
int aeGetSetSize(aeEventLoop *eventLoop) {
return eventLoop->setsize;
}
/* Resize the maximum set size of the event loop.
* If the requested set size is smaller than the current set size, but
* there is already a file descriptor in use that is >= the requested
* set size minus one, AE_ERR is returned and the operation is not
* performed at all.
*
* Otherwise AE_OK is returned and the operation is successful. */
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
int i;
if (setsize == eventLoop->setsize) return AE_OK;
if (eventLoop->maxfd >= setsize) return AE_ERR;
if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
eventLoop->setsize = setsize;
/* Make sure that if we created new slots, they are initialized with
* an AE_NONE mask. */
for (i = eventLoop->maxfd+1; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return AE_OK;
}
void aeDeleteEventLoop(aeEventLoop *eventLoop) { void aeDeleteEventLoop(aeEventLoop *eventLoop) {
aeApiFree(eventLoop); aeApiFree(eventLoop);
zfree(eventLoop->events); zfree(eventLoop->events);
@ -126,8 +156,9 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{ {
if (fd >= eventLoop->setsize) return; if (fd >= eventLoop->setsize) return;
aeFileEvent *fe = &eventLoop->events[fd]; aeFileEvent *fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return; if (fe->mask == AE_NONE) return;
aeApiDelEvent(eventLoop, fd, mask);
fe->mask = fe->mask & (~mask); fe->mask = fe->mask & (~mask);
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) { if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
/* Update the max fd */ /* Update the max fd */
@ -137,7 +168,6 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
if (eventLoop->events[j].mask != AE_NONE) break; if (eventLoop->events[j].mask != AE_NONE) break;
eventLoop->maxfd = j; eventLoop->maxfd = j;
} }
aeApiDelEvent(eventLoop, fd, mask);
} }
int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
@ -191,21 +221,12 @@ long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
{ {
aeTimeEvent *te, *prev = NULL; aeTimeEvent *te = eventLoop->timeEventHead;
te = eventLoop->timeEventHead;
while(te) { while(te) {
if (te->id == id) { if (te->id == id) {
if (prev == NULL) te->id = AE_DELETED_EVENT_ID;
eventLoop->timeEventHead = te->next;
else
prev->next = te->next;
if (te->finalizerProc)
te->finalizerProc(eventLoop, te->clientData);
zfree(te);
return AE_OK; return AE_OK;
} }
prev = te;
te = te->next; te = te->next;
} }
return AE_ERR; /* NO event with the specified ID found */ return AE_ERR; /* NO event with the specified ID found */
@ -240,7 +261,7 @@ static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
/* Process time events */ /* Process time events */
static int processTimeEvents(aeEventLoop *eventLoop) { static int processTimeEvents(aeEventLoop *eventLoop) {
int processed = 0; int processed = 0;
aeTimeEvent *te; aeTimeEvent *te, *prev;
long long maxId; long long maxId;
time_t now = time(NULL); time_t now = time(NULL);
@ -261,12 +282,32 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
} }
eventLoop->lastTime = now; eventLoop->lastTime = now;
prev = NULL;
te = eventLoop->timeEventHead; te = eventLoop->timeEventHead;
maxId = eventLoop->timeEventNextId-1; maxId = eventLoop->timeEventNextId-1;
while(te) { while(te) {
long now_sec, now_ms; long now_sec, now_ms;
long long id; long long id;
/* Remove events scheduled for deletion. */
if (te->id == AE_DELETED_EVENT_ID) {
aeTimeEvent *next = te->next;
if (prev == NULL)
eventLoop->timeEventHead = te->next;
else
prev->next = te->next;
if (te->finalizerProc)
te->finalizerProc(eventLoop, te->clientData);
zfree(te);
te = next;
continue;
}
/* Make sure we don't process time events created by time events in
* this iteration. Note that this check is currently useless: we always
* add new timers on the head, however if we change the implementation
* detail, this check may be useful again: we keep it here for future
* defense. */
if (te->id > maxId) { if (te->id > maxId) {
te = te->next; te = te->next;
continue; continue;
@ -280,36 +321,22 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
id = te->id; id = te->id;
retval = te->timeProc(eventLoop, id, te->clientData); retval = te->timeProc(eventLoop, id, te->clientData);
processed++; processed++;
/* After an event is processed our time event list may
* no longer be the same, so we restart from head.
* Still we make sure to don't process events registered
* by event handlers itself in order to don't loop forever.
* To do so we saved the max ID we want to handle.
*
* FUTURE OPTIMIZATIONS:
* Note that this is NOT great algorithmically. Redis uses
* a single time event so it's not a problem but the right
* way to do this is to add the new elements on head, and
* to flag deleted elements in a special way for later
* deletion (putting references to the nodes to delete into
* another linked list). */
if (retval != AE_NOMORE) { if (retval != AE_NOMORE) {
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
} else { } else {
aeDeleteTimeEvent(eventLoop, id); te->id = AE_DELETED_EVENT_ID;
} }
te = eventLoop->timeEventHead; }
} else { prev = te;
te = te->next; te = te->next;
} }
}
return processed; return processed;
} }
/* Process every pending time event, then every pending file event /* Process every pending time event, then every pending file event
* (that may be registered by time event callbacks just processed). * (that may be registered by time event callbacks just processed).
* Without special flags the function sleeps until some file event * Without special flags the function sleeps until some file event
* fires, or when the next time event occurrs (if any). * fires, or when the next time event occurs (if any).
* *
* If flags is 0, the function does nothing and returns. * If flags is 0, the function does nothing and returns.
* if flags has AE_ALL_EVENTS set, all the kind of events are processed. * if flags has AE_ALL_EVENTS set, all the kind of events are processed.
@ -341,22 +368,25 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
if (shortest) { if (shortest) {
long now_sec, now_ms; long now_sec, now_ms;
/* Calculate the time missing for the nearest
* timer to fire. */
aeGetTime(&now_sec, &now_ms); aeGetTime(&now_sec, &now_ms);
tvp = &tv; tvp = &tv;
tvp->tv_sec = shortest->when_sec - now_sec;
if (shortest->when_ms < now_ms) { /* How many milliseconds we need to wait for the next
tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000; * time event to fire? */
tvp->tv_sec --; long long ms =
(shortest->when_sec - now_sec)*1000 +
shortest->when_ms - now_ms;
if (ms > 0) {
tvp->tv_sec = ms/1000;
tvp->tv_usec = (ms % 1000)*1000;
} else { } else {
tvp->tv_usec = (shortest->when_ms - now_ms)*1000; tvp->tv_sec = 0;
tvp->tv_usec = 0;
} }
if (tvp->tv_sec < 0) tvp->tv_sec = 0;
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
} else { } else {
/* If we have to check for events but need to return /* If we have to check for events but need to return
* ASAP because of AE_DONT_WAIT we need to se the timeout * ASAP because of AE_DONT_WAIT we need to set the timeout
* to zero */ * to zero */
if (flags & AE_DONT_WAIT) { if (flags & AE_DONT_WAIT) {
tv.tv_sec = tv.tv_usec = 0; tv.tv_sec = tv.tv_usec = 0;
@ -395,7 +425,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
return processed; /* return the number of processed file/time events */ return processed; /* return the number of processed file/time events */
} }
/* Wait for millseconds until the given file descriptor becomes /* Wait for milliseconds until the given file descriptor becomes
* writable/readable/exception */ * writable/readable/exception */
int aeWait(int fd, int mask, long long milliseconds) { int aeWait(int fd, int mask, long long milliseconds) {
struct pollfd pfd; struct pollfd pfd;

View File

@ -33,6 +33,8 @@
#ifndef __AE_H__ #ifndef __AE_H__
#define __AE_H__ #define __AE_H__
#include <time.h>
#define AE_OK 0 #define AE_OK 0
#define AE_ERR -1 #define AE_ERR -1
@ -46,6 +48,7 @@
#define AE_DONT_WAIT 4 #define AE_DONT_WAIT 4
#define AE_NOMORE -1 #define AE_NOMORE -1
#define AE_DELETED_EVENT_ID -1
/* Macros */ /* Macros */
#define AE_NOTUSED(V) ((void) V) #define AE_NOTUSED(V) ((void) V)
@ -114,5 +117,7 @@ int aeWait(int fd, int mask, long long milliseconds);
void aeMain(aeEventLoop *eventLoop); void aeMain(aeEventLoop *eventLoop);
char *aeGetApiName(void); char *aeGetApiName(void);
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep); void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
int aeGetSetSize(aeEventLoop *eventLoop);
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
#endif #endif

View File

@ -45,7 +45,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree(state); zfree(state);
return -1; return -1;
} }
state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */ state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
if (state->epfd == -1) { if (state->epfd == -1) {
zfree(state->events); zfree(state->events);
zfree(state); zfree(state);
@ -55,6 +55,13 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
return 0; return 0;
} }
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
aeApiState *state = eventLoop->apidata;
state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);
return 0;
}
static void aeApiFree(aeEventLoop *eventLoop) { static void aeApiFree(aeEventLoop *eventLoop) {
aeApiState *state = eventLoop->apidata; aeApiState *state = eventLoop->apidata;
@ -65,7 +72,7 @@ static void aeApiFree(aeEventLoop *eventLoop) {
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
aeApiState *state = eventLoop->apidata; aeApiState *state = eventLoop->apidata;
struct epoll_event ee; struct epoll_event ee = {0}; /* avoid valgrind warning */
/* If the fd was already monitored for some event, we need a MOD /* If the fd was already monitored for some event, we need a MOD
* operation. Otherwise we need an ADD operation. */ * operation. Otherwise we need an ADD operation. */
int op = eventLoop->events[fd].mask == AE_NONE ? int op = eventLoop->events[fd].mask == AE_NONE ?
@ -75,7 +82,6 @@ static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
mask |= eventLoop->events[fd].mask; /* Merge old events */ mask |= eventLoop->events[fd].mask; /* Merge old events */
if (mask & AE_READABLE) ee.events |= EPOLLIN; if (mask & AE_READABLE) ee.events |= EPOLLIN;
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
ee.data.u64 = 0; /* avoid valgrind warning */
ee.data.fd = fd; ee.data.fd = fd;
if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
return 0; return 0;
@ -83,13 +89,12 @@ static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
aeApiState *state = eventLoop->apidata; aeApiState *state = eventLoop->apidata;
struct epoll_event ee; struct epoll_event ee = {0}; /* avoid valgrind warning */
int mask = eventLoop->events[fd].mask & (~delmask); int mask = eventLoop->events[fd].mask & (~delmask);
ee.events = 0; ee.events = 0;
if (mask & AE_READABLE) ee.events |= EPOLLIN; if (mask & AE_READABLE) ee.events |= EPOLLIN;
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
ee.data.u64 = 0; /* avoid valgrind warning */
ee.data.fd = fd; ee.data.fd = fd;
if (mask != AE_NONE) { if (mask != AE_NONE) {
epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee); epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);

View File

@ -50,15 +50,15 @@ static int evport_debug = 0;
* aeApiPoll, the corresponding file descriptors become dissociated from the * aeApiPoll, the corresponding file descriptors become dissociated from the
* port. This is necessary because poll events are level-triggered, so if the * port. This is necessary because poll events are level-triggered, so if the
* fd didn't become dissociated, it would immediately fire another event since * fd didn't become dissociated, it would immediately fire another event since
* the underlying state hasn't changed yet. We must reassociate the file * the underlying state hasn't changed yet. We must re-associate the file
* descriptor, but only after we know that our caller has actually read from it. * descriptor, but only after we know that our caller has actually read from it.
* The ae API does not tell us exactly when that happens, but we do know that * The ae API does not tell us exactly when that happens, but we do know that
* it must happen by the time aeApiPoll is called again. Our solution is to * it must happen by the time aeApiPoll is called again. Our solution is to
* keep track of the last fds returned by aeApiPoll and reassociate them next * keep track of the last fds returned by aeApiPoll and re-associate them next
* time aeApiPoll is invoked. * time aeApiPoll is invoked.
* *
* To summarize, in this module, each fd association is EITHER (a) represented * To summarize, in this module, each fd association is EITHER (a) represented
* only via the in-kernel assocation OR (b) represented by pending_fds and * only via the in-kernel association OR (b) represented by pending_fds and
* pending_masks. (b) is only true for the last fds we returned from aeApiPoll, * pending_masks. (b) is only true for the last fds we returned from aeApiPoll,
* and only until we enter aeApiPoll again (at which point we restore the * and only until we enter aeApiPoll again (at which point we restore the
* in-kernel association). * in-kernel association).
@ -94,6 +94,11 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
return 0; return 0;
} }
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
/* Nothing to resize here. */
return 0;
}
static void aeApiFree(aeEventLoop *eventLoop) { static void aeApiFree(aeEventLoop *eventLoop) {
aeApiState *state = eventLoop->apidata; aeApiState *state = eventLoop->apidata;
@ -164,7 +169,7 @@ static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
* This fd was recently returned from aeApiPoll. It should be safe to * This fd was recently returned from aeApiPoll. It should be safe to
* assume that the consumer has processed that poll event, but we play * assume that the consumer has processed that poll event, but we play
* it safer by simply updating pending_mask. The fd will be * it safer by simply updating pending_mask. The fd will be
* reassociated as usual when aeApiPoll is called again. * re-associated as usual when aeApiPoll is called again.
*/ */
if (evport_debug) if (evport_debug)
fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd); fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd);
@ -228,7 +233,7 @@ static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
* ENOMEM is a potentially transient condition, but the kernel won't * ENOMEM is a potentially transient condition, but the kernel won't
* generally return it unless things are really bad. EAGAIN indicates * generally return it unless things are really bad. EAGAIN indicates
* we've reached an resource limit, for which it doesn't make sense to * we've reached an resource limit, for which it doesn't make sense to
* retry (counterintuitively). All other errors indicate a bug. In any * retry (counter-intuitively). All other errors indicate a bug. In any
* of these cases, the best we can do is to abort. * of these cases, the best we can do is to abort.
*/ */
abort(); /* will not return */ abort(); /* will not return */
@ -243,7 +248,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
port_event_t event[MAX_EVENT_BATCHSZ]; port_event_t event[MAX_EVENT_BATCHSZ];
/* /*
* If we've returned fd events before, we must reassociate them with the * If we've returned fd events before, we must re-associate them with the
* port now, before calling port_get(). See the block comment at the top of * port now, before calling port_get(). See the block comment at the top of
* this file for an explanation of why. * this file for an explanation of why.
*/ */

View File

@ -54,7 +54,13 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
return -1; return -1;
} }
eventLoop->apidata = state; eventLoop->apidata = state;
return 0;
}
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
aeApiState *state = eventLoop->apidata;
state->events = zrealloc(state->events, sizeof(struct kevent)*setsize);
return 0; return 0;
} }

View File

@ -29,6 +29,7 @@
*/ */
#include <sys/select.h>
#include <string.h> #include <string.h>
typedef struct aeApiState { typedef struct aeApiState {
@ -48,6 +49,12 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
return 0; return 0;
} }
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
/* Just ensure we have enough room in the fd_set type. */
if (setsize >= FD_SETSIZE) return -1;
return 0;
}
static void aeApiFree(aeEventLoop *eventLoop) { static void aeApiFree(aeEventLoop *eventLoop) {
zfree(eventLoop->apidata); zfree(eventLoop->apidata);
} }

94
src/atomicvar.h Normal file
View File

@ -0,0 +1,94 @@
/* This file implements atomic counters using __atomic or __sync macros if
* available, otherwise synchronizing different threads using a mutex.
*
* The exported interaface is composed of three macros:
*
* atomicIncr(var,count,mutex) -- Increment the atomic counter
* atomicDecr(var,count,mutex) -- Decrement the atomic counter
* atomicGet(var,dstvar,mutex) -- Fetch the atomic counter value
*
* If atomic primitives are availble (tested in config.h) the mutex
* is not used.
*
* Never use return value from the macros. To update and get use instead:
*
* atomicIncr(mycounter,...);
* atomicGet(mycounter,newvalue);
* doSomethingWith(newvalue);
*
* ----------------------------------------------------------------------------
*
* Copyright (c) 2015, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <pthread.h>
#ifndef __ATOMIC_VAR_H
#define __ATOMIC_VAR_H
#if defined(__ATOMIC_RELAXED) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)
/* Implementation using __atomic macros. */
#define atomicIncr(var,count,mutex) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
#define atomicDecr(var,count,mutex) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED)
#define atomicGet(var,dstvar,mutex) do { \
dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \
} while(0)
#elif defined(HAVE_ATOMIC)
/* Implementation using __sync macros. */
#define atomicIncr(var,count,mutex) __sync_add_and_fetch(&var,(count))
#define atomicDecr(var,count,mutex) __sync_sub_and_fetch(&var,(count))
#define atomicGet(var,dstvar,mutex) do { \
dstvar = __sync_sub_and_fetch(&var,0); \
} while(0)
#else
/* Implementation using pthread mutex. */
#define atomicIncr(var,count,mutex) do { \
pthread_mutex_lock(&mutex); \
var += (count); \
pthread_mutex_unlock(&mutex); \
} while(0)
#define atomicDecr(var,count,mutex) do { \
pthread_mutex_lock(&mutex); \
var -= (count); \
pthread_mutex_unlock(&mutex); \
} while(0)
#define atomicGet(var,dstvar,mutex) do { \
pthread_mutex_lock(&mutex); \
dstvar = var; \
pthread_mutex_unlock(&mutex); \
} while(0)
#endif
#endif /* __ATOMIC_VAR_H */

View File

@ -64,8 +64,7 @@ do { \
return (V); \ return (V); \
} while (0); } while (0);
#define REEXECUTE() \ #define REEXECUTE() \
--p; \ goto reexecute; \
break;
#ifdef __GNUC__ #ifdef __GNUC__
@ -401,6 +400,8 @@ enum http_host_state
, s_http_host , s_http_host
, s_http_host_v6 , s_http_host_v6
, s_http_host_v6_end , s_http_host_v6_end
, s_http_host_v6_zone_start
, s_http_host_v6_zone
, s_http_host_port_start , s_http_host_port_start
, s_http_host_port , s_http_host_port
}; };
@ -434,6 +435,12 @@ enum http_host_state
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif #endif
/**
* Verify that a char is a valid visible (printable) US-ASCII
* character or %x80-FF
**/
#define IS_HEADER_CHAR(ch) \
(ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
@ -638,6 +645,7 @@ size_t http_parser_execute (http_parser *parser,
const char *body_mark = 0; const char *body_mark = 0;
const char *status_mark = 0; const char *status_mark = 0;
enum state p_state = (enum state) parser->state; enum state p_state = (enum state) parser->state;
const unsigned int lenient = parser->lenient_http_headers;
/* We're in an error state. Don't bother doing anything. */ /* We're in an error state. Don't bother doing anything. */
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
@ -697,6 +705,7 @@ size_t http_parser_execute (http_parser *parser,
if (PARSING_HEADER(CURRENT_STATE())) if (PARSING_HEADER(CURRENT_STATE()))
COUNT_HEADER_SIZE(1); COUNT_HEADER_SIZE(1);
reexecute:
switch (CURRENT_STATE()) { switch (CURRENT_STATE()) {
case s_dead: case s_dead:
@ -957,21 +966,23 @@ size_t http_parser_execute (http_parser *parser,
parser->method = (enum http_method) 0; parser->method = (enum http_method) 0;
parser->index = 1; parser->index = 1;
switch (ch) { switch (ch) {
case 'A': parser->method = HTTP_ACL; break;
case 'B': parser->method = HTTP_BIND; break;
case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
case 'D': parser->method = HTTP_DELETE; break; case 'D': parser->method = HTTP_DELETE; break;
case 'G': parser->method = HTTP_GET; break; case 'G': parser->method = HTTP_GET; break;
case 'H': parser->method = HTTP_HEAD; break; case 'H': parser->method = HTTP_HEAD; break;
case 'L': parser->method = HTTP_LOCK; break; case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
case 'N': parser->method = HTTP_NOTIFY; break; case 'N': parser->method = HTTP_NOTIFY; break;
case 'O': parser->method = HTTP_OPTIONS; break; case 'O': parser->method = HTTP_OPTIONS; break;
case 'P': parser->method = HTTP_POST; case 'P': parser->method = HTTP_POST;
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
break; break;
case 'R': parser->method = HTTP_REPORT; break; case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
case 'T': parser->method = HTTP_TRACE; break; case 'T': parser->method = HTTP_TRACE; break;
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
default: default:
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
@ -996,69 +1007,40 @@ size_t http_parser_execute (http_parser *parser,
UPDATE_STATE(s_req_spaces_before_url); UPDATE_STATE(s_req_spaces_before_url);
} else if (ch == matcher[parser->index]) { } else if (ch == matcher[parser->index]) {
; /* nada */ ; /* nada */
} else if (parser->method == HTTP_CONNECT) { } else if (IS_ALPHA(ch)) {
if (parser->index == 1 && ch == 'H') {
parser->method = HTTP_CHECKOUT; switch (parser->method << 16 | parser->index << 8 | ch) {
} else if (parser->index == 2 && ch == 'P') { #define XX(meth, pos, ch, new_meth) \
parser->method = HTTP_COPY; case (HTTP_##meth << 16 | pos << 8 | ch): \
} else { parser->method = HTTP_##new_meth; break;
XX(POST, 1, 'U', PUT)
XX(POST, 1, 'A', PATCH)
XX(CONNECT, 1, 'H', CHECKOUT)
XX(CONNECT, 2, 'P', COPY)
XX(MKCOL, 1, 'O', MOVE)
XX(MKCOL, 1, 'E', MERGE)
XX(MKCOL, 2, 'A', MKACTIVITY)
XX(MKCOL, 3, 'A', MKCALENDAR)
XX(SUBSCRIBE, 1, 'E', SEARCH)
XX(REPORT, 2, 'B', REBIND)
XX(POST, 1, 'R', PROPFIND)
XX(PROPFIND, 4, 'P', PROPPATCH)
XX(PUT, 2, 'R', PURGE)
XX(LOCK, 1, 'I', LINK)
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
XX(UNLOCK, 2, 'B', UNBIND)
XX(UNLOCK, 3, 'I', UNLINK)
#undef XX
default:
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
} }
} else if (parser->method == HTTP_MKCOL) { } else if (ch == '-' &&
if (parser->index == 1 && ch == 'O') { parser->index == 1 &&
parser->method = HTTP_MOVE; parser->method == HTTP_MKCOL) {
} else if (parser->index == 1 && ch == 'E') {
parser->method = HTTP_MERGE;
} else if (parser->index == 1 && ch == '-') {
parser->method = HTTP_MSEARCH; parser->method = HTTP_MSEARCH;
} else if (parser->index == 2 && ch == 'A') {
parser->method = HTTP_MKACTIVITY;
} else if (parser->index == 3 && ch == 'A') {
parser->method = HTTP_MKCALENDAR;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->method == HTTP_SUBSCRIBE) {
if (parser->index == 1 && ch == 'E') {
parser->method = HTTP_SEARCH;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->index == 1 && parser->method == HTTP_POST) {
if (ch == 'R') {
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
} else if (ch == 'U') {
parser->method = HTTP_PUT; /* or HTTP_PURGE */
} else if (ch == 'A') {
parser->method = HTTP_PATCH;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->index == 2) {
if (parser->method == HTTP_PUT) {
if (ch == 'R') {
parser->method = HTTP_PURGE;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->method == HTTP_UNLOCK) {
if (ch == 'S') {
parser->method = HTTP_UNSUBSCRIBE;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
parser->method = HTTP_PROPPATCH;
} else { } else {
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
@ -1487,6 +1469,12 @@ size_t http_parser_execute (http_parser *parser,
goto error; goto error;
} }
if (parser->flags & F_CONTENTLENGTH) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
parser->flags |= F_CONTENTLENGTH;
parser->content_length = ch - '0'; parser->content_length = ch - '0';
break; break;
@ -1536,6 +1524,11 @@ size_t http_parser_execute (http_parser *parser,
REEXECUTE(); REEXECUTE();
} }
if (!lenient && !IS_HEADER_CHAR(ch)) {
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error;
}
c = LOWER(ch); c = LOWER(ch);
switch (h_state) { switch (h_state) {
@ -1703,7 +1696,10 @@ size_t http_parser_execute (http_parser *parser,
case s_header_almost_done: case s_header_almost_done:
{ {
STRICT_CHECK(ch != LF); if (UNLIKELY(ch != LF)) {
SET_ERRNO(HPE_LF_EXPECTED);
goto error;
}
UPDATE_STATE(s_header_value_lws); UPDATE_STATE(s_header_value_lws);
break; break;
@ -1782,9 +1778,17 @@ size_t http_parser_execute (http_parser *parser,
if (parser->flags & F_TRAILING) { if (parser->flags & F_TRAILING) {
/* End of a chunked request */ /* End of a chunked request */
UPDATE_STATE(NEW_MESSAGE()); UPDATE_STATE(s_message_done);
CALLBACK_NOTIFY(message_complete); CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
break; REEXECUTE();
}
/* Cannot use chunked encoding and a content-length header together
per the HTTP specification. */
if ((parser->flags & F_CHUNKED) &&
(parser->flags & F_CONTENTLENGTH)) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
} }
UPDATE_STATE(s_headers_done); UPDATE_STATE(s_headers_done);
@ -1809,6 +1813,9 @@ size_t http_parser_execute (http_parser *parser,
case 0: case 0:
break; break;
case 2:
parser->upgrade = 1;
case 1: case 1:
parser->flags |= F_SKIPBODY; parser->flags |= F_SKIPBODY;
break; break;
@ -1828,12 +1835,16 @@ size_t http_parser_execute (http_parser *parser,
case s_headers_done: case s_headers_done:
{ {
int hasBody;
STRICT_CHECK(ch != LF); STRICT_CHECK(ch != LF);
parser->nread = 0; parser->nread = 0;
/* Exit, the rest of the connect is in a different protocol. */ hasBody = parser->flags & F_CHUNKED ||
if (parser->upgrade) { (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
(parser->flags & F_SKIPBODY) || !hasBody)) {
/* Exit, the rest of the message is in a different protocol. */
UPDATE_STATE(NEW_MESSAGE()); UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete); CALLBACK_NOTIFY(message_complete);
RETURN((p - data) + 1); RETURN((p - data) + 1);
@ -1854,8 +1865,7 @@ size_t http_parser_execute (http_parser *parser,
/* Content-Length header given and non-zero */ /* Content-Length header given and non-zero */
UPDATE_STATE(s_body_identity); UPDATE_STATE(s_body_identity);
} else { } else {
if (parser->type == HTTP_REQUEST || if (!http_message_needs_eof(parser)) {
!http_message_needs_eof(parser)) {
/* Assume content-length 0 - read the next */ /* Assume content-length 0 - read the next */
UPDATE_STATE(NEW_MESSAGE()); UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete); CALLBACK_NOTIFY(message_complete);
@ -1915,6 +1925,10 @@ size_t http_parser_execute (http_parser *parser,
case s_message_done: case s_message_done:
UPDATE_STATE(NEW_MESSAGE()); UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete); CALLBACK_NOTIFY(message_complete);
if (parser->upgrade) {
/* Exit, the rest of the message is in a different protocol. */
RETURN((p - data) + 1);
}
break; break;
case s_chunk_size_start: case s_chunk_size_start:
@ -1994,6 +2008,7 @@ size_t http_parser_execute (http_parser *parser,
} else { } else {
UPDATE_STATE(s_chunk_data); UPDATE_STATE(s_chunk_data);
} }
CALLBACK_NOTIFY(chunk_header);
break; break;
} }
@ -2033,6 +2048,7 @@ size_t http_parser_execute (http_parser *parser,
STRICT_CHECK(ch != LF); STRICT_CHECK(ch != LF);
parser->nread = 0; parser->nread = 0;
UPDATE_STATE(s_chunk_size_start); UPDATE_STATE(s_chunk_size_start);
CALLBACK_NOTIFY(chunk_complete);
break; break;
default: default:
@ -2136,15 +2152,21 @@ http_parser_init (http_parser *parser, enum http_parser_type t)
parser->http_errno = HPE_OK; parser->http_errno = HPE_OK;
} }
void
http_parser_settings_init(http_parser_settings *settings)
{
memset(settings, 0, sizeof(*settings));
}
const char * const char *
http_errno_name(enum http_errno err) { http_errno_name(enum http_errno err) {
assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
return http_strerror_tab[err].name; return http_strerror_tab[err].name;
} }
const char * const char *
http_errno_description(enum http_errno err) { http_errno_description(enum http_errno err) {
assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
return http_strerror_tab[err].description; return http_strerror_tab[err].description;
} }
@ -2197,6 +2219,23 @@ http_parse_host_char(enum http_host_state s, const char ch) {
return s_http_host_v6; return s_http_host_v6;
} }
if (s == s_http_host_v6 && ch == '%') {
return s_http_host_v6_zone_start;
}
break;
case s_http_host_v6_zone:
if (ch == ']') {
return s_http_host_v6_end;
}
/* FALLTHROUGH */
case s_http_host_v6_zone_start:
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
ch == '~') {
return s_http_host_v6_zone;
}
break; break;
case s_http_host_port: case s_http_host_port:
@ -2220,6 +2259,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
const char *p; const char *p;
size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
assert(u->field_set & (1 << UF_HOST));
u->field_data[UF_HOST].len = 0; u->field_data[UF_HOST].len = 0;
s = found_at ? s_http_userinfo_start : s_http_host_start; s = found_at ? s_http_userinfo_start : s_http_host_start;
@ -2246,6 +2287,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
u->field_data[UF_HOST].len++; u->field_data[UF_HOST].len++;
break; break;
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
u->field_data[UF_HOST].len++;
break;
case s_http_host_port: case s_http_host_port:
if (s != s_http_host_port) { if (s != s_http_host_port) {
u->field_data[UF_PORT].off = p - buf; u->field_data[UF_PORT].off = p - buf;
@ -2275,6 +2321,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
case s_http_host_start: case s_http_host_start:
case s_http_host_v6_start: case s_http_host_v6_start:
case s_http_host_v6: case s_http_host_v6:
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
case s_http_host_port_start: case s_http_host_port_start:
case s_http_userinfo: case s_http_userinfo:
case s_http_userinfo_start: case s_http_userinfo_start:
@ -2286,6 +2334,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
return 0; return 0;
} }
void
http_parser_url_init(struct http_parser_url *u) {
memset(u, 0, sizeof(*u));
}
int int
http_parser_parse_url(const char *buf, size_t buflen, int is_connect, http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
struct http_parser_url *u) struct http_parser_url *u)
@ -2359,7 +2412,12 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
/* host must be present if there is a schema */ /* host must be present if there is a schema */
/* parsing http:///toto will fail */ /* parsing http:///toto will fail */
if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { if ((u->field_set & (1 << UF_SCHEMA)) &&
(u->field_set & (1 << UF_HOST)) == 0) {
return 1;
}
if (u->field_set & (1 << UF_HOST)) {
if (http_parse_host(buf, u, found_at) != 0) { if (http_parse_host(buf, u, found_at) != 0) {
return 1; return 1;
} }

View File

@ -26,11 +26,12 @@ extern "C" {
/* Also update SONAME in the Makefile whenever you change these. */ /* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2 #define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 4 #define HTTP_PARSER_VERSION_MINOR 7
#define HTTP_PARSER_VERSION_PATCH 2 #define HTTP_PARSER_VERSION_PATCH 1
#include <sys/types.h> #include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) #if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h> #include <BaseTsd.h>
#include <stddef.h> #include <stddef.h>
typedef __int8 int8_t; typedef __int8 int8_t;
@ -76,6 +77,11 @@ typedef struct http_parser_settings http_parser_settings;
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body. * chunked' headers that indicate the presence of a body.
* *
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily * http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url" * many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data. * each providing just a few characters more data.
@ -84,6 +90,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*); typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */ /* Request Methods */
#define HTTP_METHOD_MAP(XX) \ #define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \ XX(0, DELETE, DELETE) \
@ -95,7 +171,7 @@ typedef int (*http_cb) (http_parser*);
XX(5, CONNECT, CONNECT) \ XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \ XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \ XX(7, TRACE, TRACE) \
/* webdav */ \ /* WebDAV */ \
XX(8, COPY, COPY) \ XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \ XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \ XX(10, MKCOL, MKCOL) \
@ -104,21 +180,28 @@ typedef int (*http_cb) (http_parser*);
XX(13, PROPPATCH, PROPPATCH) \ XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \ XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \ XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \ /* subversion */ \
XX(16, REPORT, REPORT) \ XX(20, REPORT, REPORT) \
XX(17, MKACTIVITY, MKACTIVITY) \ XX(21, MKACTIVITY, MKACTIVITY) \
XX(18, CHECKOUT, CHECKOUT) \ XX(22, CHECKOUT, CHECKOUT) \
XX(19, MERGE, MERGE) \ XX(23, MERGE, MERGE) \
/* upnp */ \ /* upnp */ \
XX(20, MSEARCH, M-SEARCH) \ XX(24, MSEARCH, M-SEARCH) \
XX(21, NOTIFY, NOTIFY) \ XX(25, NOTIFY, NOTIFY) \
XX(22, SUBSCRIBE, SUBSCRIBE) \ XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \ /* RFC-5789 */ \
XX(24, PATCH, PATCH) \ XX(28, PATCH, PATCH) \
XX(25, PURGE, PURGE) \ XX(29, PURGE, PURGE) \
/* CalDAV */ \ /* CalDAV */ \
XX(26, MKCALENDAR, MKCALENDAR) \ XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
enum http_method enum http_method
{ {
@ -140,6 +223,7 @@ enum flags
, F_TRAILING = 1 << 4 , F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5 , F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6 , F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
}; };
@ -160,6 +244,8 @@ enum flags
XX(CB_body, "the on_body callback failed") \ XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \ XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \ XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\ \
/* Parsing-related errors */ \ /* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
@ -180,6 +266,8 @@ enum flags
XX(INVALID_HEADER_TOKEN, "invalid character in header") \ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \ XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \ "invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \ XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \ "invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \ XX(INVALID_CONSTANT, "invalid constant string") \
@ -204,10 +292,11 @@ enum http_errno {
struct http_parser { struct http_parser {
/** PRIVATE **/ /** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */ unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 8; /* enum state from http_parser.c */ unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 8; /* enum header_state from http_parser.c */ unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 8; /* index into current matcher */ unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */ uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
@ -240,6 +329,11 @@ struct http_parser_settings {
http_cb on_headers_complete; http_cb on_headers_complete;
http_data_cb on_body; http_data_cb on_body;
http_cb on_message_complete; http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
}; };
@ -288,6 +382,11 @@ unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type); void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets /* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */ * `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser, size_t http_parser_execute(http_parser *parser,
@ -313,6 +412,9 @@ const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */ /* Return a string description of the given error */
const char *http_errno_description(enum http_errno err); const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */ /* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen, int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect, int is_connect,

View File

@ -30,10 +30,20 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
/* This function provide us access to the original libc free(). This is useful
* for instance to free results obtained by backtrace_symbols(). We need
* to define this function before including zmalloc.h that may shadow the
* free implementation if we use jemalloc or another non standard allocator. */
void zlibc_free(void *ptr) {
free(ptr);
}
#include <string.h> #include <string.h>
#include <pthread.h> #include <pthread.h>
#include "config.h" #include "config.h"
#include "zmalloc.h" #include "zmalloc.h"
#include "atomicvar.h"
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0) #define PREFIX_SIZE (0)
@ -56,15 +66,15 @@
#define calloc(count,size) je_calloc(count,size) #define calloc(count,size) je_calloc(count,size)
#define realloc(ptr,size) je_realloc(ptr,size) #define realloc(ptr,size) je_realloc(ptr,size)
#define free(ptr) je_free(ptr) #define free(ptr) je_free(ptr)
#define mallocx(size,flags) je_mallocx(size,flags)
#define dallocx(ptr,flags) je_dallocx(ptr,flags)
#endif #endif
#define update_zmalloc_stat_alloc(__n,__size) do { \ #define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \ size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \ if (zmalloc_thread_safe) { \
pthread_mutex_lock(&used_memory_mutex); \ atomicIncr(used_memory,__n,used_memory_mutex); \
used_memory += _n; \
pthread_mutex_unlock(&used_memory_mutex); \
} else { \ } else { \
used_memory += _n; \ used_memory += _n; \
} \ } \
@ -74,9 +84,7 @@
size_t _n = (__n); \ size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \ if (zmalloc_thread_safe) { \
pthread_mutex_lock(&used_memory_mutex); \ atomicDecr(used_memory,__n,used_memory_mutex); \
used_memory -= _n; \
pthread_mutex_unlock(&used_memory_mutex); \
} else { \ } else { \
used_memory -= _n; \ used_memory -= _n; \
} \ } \
@ -86,37 +94,57 @@ static size_t used_memory = 0;
static int zmalloc_thread_safe = 0; static int zmalloc_thread_safe = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
static void zmalloc_oom(size_t size) { static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size); size);
fflush(stderr); fflush(stderr);
abort(); abort();
} }
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
void *zmalloc(size_t size) { void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE); void *ptr = malloc(size+PREFIX_SIZE);
if (!ptr) zmalloc_oom(size); if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr),size); update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr; return ptr;
#else #else
*((size_t*)ptr) = size; *((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE,size); update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE; return (char*)ptr+PREFIX_SIZE;
#endif #endif
} }
/* Allocation and free functions that bypass the thread cache
* and go straight to the allocator arena bins.
* Currently implemented only for jemalloc. Used for online defragmentation. */
#ifdef HAVE_DEFRAG
void *zmalloc_no_tcache(size_t size) {
void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);
if (!ptr) zmalloc_oom_handler(size);
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr;
}
void zfree_no_tcache(void *ptr) {
if (ptr == NULL) return;
update_zmalloc_stat_free(zmalloc_size(ptr));
dallocx(ptr, MALLOCX_TCACHE_NONE);
}
#endif
void *zcalloc(size_t size) { void *zcalloc(size_t size) {
void *ptr = calloc(1, size+PREFIX_SIZE); void *ptr = calloc(1, size+PREFIX_SIZE);
if (!ptr) zmalloc_oom(size); if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr),size); update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr; return ptr;
#else #else
*((size_t*)ptr) = size; *((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE,size); update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE; return (char*)ptr+PREFIX_SIZE;
#endif #endif
} }
@ -132,26 +160,26 @@ void *zrealloc(void *ptr, size_t size) {
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
oldsize = zmalloc_size(ptr); oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size); newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom(size); if (!newptr) zmalloc_oom_handler(size);
update_zmalloc_stat_free(oldsize); update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(zmalloc_size(newptr),size); update_zmalloc_stat_alloc(zmalloc_size(newptr));
return newptr; return newptr;
#else #else
realptr = (char*)ptr-PREFIX_SIZE; realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr); oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+PREFIX_SIZE); newptr = realloc(realptr,size+PREFIX_SIZE);
if (!newptr) zmalloc_oom(size); if (!newptr) zmalloc_oom_handler(size);
*((size_t*)newptr) = size; *((size_t*)newptr) = size;
update_zmalloc_stat_free(oldsize); update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(size,size); update_zmalloc_stat_alloc(size);
return (char*)newptr+PREFIX_SIZE; return (char*)newptr+PREFIX_SIZE;
#endif #endif
} }
/* Provide zmalloc_size() for systems where this function is not provided by /* Provide zmalloc_size() for systems where this function is not provided by
* malloc itself, given that in that case we store an header with this * malloc itself, given that in that case we store a header with this
* information as the first bytes of every allocation. */ * information as the first bytes of every allocation. */
#ifndef HAVE_MALLOC_SIZE #ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr) { size_t zmalloc_size(void *ptr) {
@ -193,9 +221,11 @@ char *zstrdup(const char *s) {
size_t zmalloc_used_memory(void) { size_t zmalloc_used_memory(void) {
size_t um; size_t um;
if (zmalloc_thread_safe) pthread_mutex_lock(&used_memory_mutex); if (zmalloc_thread_safe) {
atomicGet(used_memory,um,used_memory_mutex);
} else {
um = used_memory; um = used_memory;
if (zmalloc_thread_safe) pthread_mutex_unlock(&used_memory_mutex); }
return um; return um;
} }
@ -203,6 +233,10 @@ void zmalloc_enable_thread_safeness(void) {
zmalloc_thread_safe = 1; zmalloc_thread_safe = 1;
} }
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
zmalloc_oom_handler = oom_handler;
}
/* Get the RSS information in an OS-specific way. /* Get the RSS information in an OS-specific way.
* *
* WARNING: the function zmalloc_get_rss() is not designed to be fast * WARNING: the function zmalloc_get_rss() is not designed to be fast
@ -211,9 +245,9 @@ void zmalloc_enable_thread_safeness(void) {
* *
* For this kind of "fast RSS reporting" usages use instead the * For this kind of "fast RSS reporting" usages use instead the
* function RedisEstimateRSS() that is a much faster (and less precise) * function RedisEstimateRSS() that is a much faster (and less precise)
* version of the funciton. */ * version of the function. */
#if defined(HAVE_PROCFS) #if defined(HAVE_PROC_STAT)
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -282,6 +316,113 @@ size_t zmalloc_get_rss(void) {
#endif #endif
/* Fragmentation = RSS / allocated-bytes */ /* Fragmentation = RSS / allocated-bytes */
float zmalloc_get_fragmentation_ratio(void) { float zmalloc_get_fragmentation_ratio(size_t rss) {
return (float)zmalloc_get_rss()/zmalloc_used_memory(); return (float)rss/zmalloc_used_memory();
} }
/* Get the sum of the specified field (converted form kb to bytes) in
* /proc/self/smaps. The field must be specified with trailing ":" as it
* apperas in the smaps output.
*
* If a pid is specified, the information is extracted for such a pid,
* otherwise if pid is -1 the information is reported is about the
* current process.
*
* Example: zmalloc_get_smap_bytes_by_field("Rss:",-1);
*/
#if defined(HAVE_PROC_SMAPS)
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
char line[1024];
size_t bytes = 0;
int flen = strlen(field);
FILE *fp;
if (pid == -1) {
fp = fopen("/proc/self/smaps","r");
} else {
char filename[128];
snprintf(filename,sizeof(filename),"/proc/%ld/smaps",pid);
fp = fopen(filename,"r");
}
if (!fp) return 0;
while(fgets(line,sizeof(line),fp) != NULL) {
if (strncmp(line,field,flen) == 0) {
char *p = strchr(line,'k');
if (p) {
*p = '\0';
bytes += strtol(line+flen,NULL,10) * 1024;
}
}
}
fclose(fp);
return bytes;
}
#else
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
((void) field);
((void) pid);
return 0;
}
#endif
size_t zmalloc_get_private_dirty(long pid) {
return zmalloc_get_smap_bytes_by_field("Private_Dirty:",pid);
}
/* Returns the size of physical memory (RAM) in bytes.
* It looks ugly, but this is the cleanest way to achive cross platform results.
* Cleaned up from:
*
* http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
*
* Note that this function:
* 1) Was released under the following CC attribution license:
* http://creativecommons.org/licenses/by/3.0/deed.en_US.
* 2) Was originally implemented by David Robert Nadeau.
* 3) Was modified for Redis by Matt Stancliff.
* 4) This note exists in order to comply with the original license.
*/
size_t zmalloc_get_memory_size(void) {
#if defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
#elif defined(HW_PHYSMEM64)
mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
#endif
int64_t size = 0; /* 64-bit */
size_t len = sizeof(size);
if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
/* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);
#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
/* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_REALMEM)
mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
#elif defined(HW_PYSMEM)
mib[1] = HW_PHYSMEM; /* Others. ------------------ */
#endif
unsigned int size = 0; /* 32-bit */
size_t len = sizeof(size);
if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */
#endif /* sysctl and sysconf variants */
#else
return 0L; /* Unknown OS. */
#endif
}

View File

@ -38,7 +38,7 @@
#if defined(USE_TCMALLOC) #if defined(USE_TCMALLOC)
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
#include <google/tcmalloc.h> #include <google/tcmalloc.h>
#if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 6 #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
#define HAVE_MALLOC_SIZE 1 #define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) tc_malloc_size(p) #define zmalloc_size(p) tc_malloc_size(p)
#else #else
@ -47,11 +47,10 @@
#elif defined(USE_JEMALLOC) #elif defined(USE_JEMALLOC)
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
#define JEMALLOC_MANGLE
#include <jemalloc/jemalloc.h> #include <jemalloc/jemalloc.h>
#if JEMALLOC_VERSION_MAJOR >= 2 && JEMALLOC_VERSION_MINOR >= 1 #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
#define HAVE_MALLOC_SIZE 1 #define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) JEMALLOC_P(malloc_usable_size)(p) #define zmalloc_size(p) je_malloc_usable_size(p)
#else #else
#error "Newer version of jemalloc required" #error "Newer version of jemalloc required"
#endif #endif
@ -66,6 +65,13 @@
#define ZMALLOC_LIB "libc" #define ZMALLOC_LIB "libc"
#endif #endif
/* We can enable the Redis defrag capabilities only if we are using Jemalloc
* and the version used is our special version modified for Redis having
* the ability to return per-allocation fragmentation hints. */
#if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)
#define HAVE_DEFRAG
#endif
void *zmalloc(size_t size); void *zmalloc(size_t size);
void *zcalloc(size_t size); void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size); void *zrealloc(void *ptr, size_t size);
@ -73,8 +79,18 @@ void zfree(void *ptr);
char *zstrdup(const char *s); char *zstrdup(const char *s);
size_t zmalloc_used_memory(void); size_t zmalloc_used_memory(void);
void zmalloc_enable_thread_safeness(void); void zmalloc_enable_thread_safeness(void);
float zmalloc_get_fragmentation_ratio(void); void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
float zmalloc_get_fragmentation_ratio(size_t rss);
size_t zmalloc_get_rss(void); size_t zmalloc_get_rss(void);
size_t zmalloc_get_private_dirty(long pid);
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
size_t zmalloc_get_memory_size(void);
void zlibc_free(void *ptr);
#ifdef HAVE_DEFRAG
void zfree_no_tcache(void *ptr);
void *zmalloc_no_tcache(size_t size);
#endif
#ifndef HAVE_MALLOC_SIZE #ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr); size_t zmalloc_size(void *ptr);