File httperf-0.9.0+git.20201206.obscpio of Package httperf
07070100000000000081A40000000000000000000000015FCD0002000000FC000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/.gitignore/data/
/nbproject/
*.o
*.a
.deps/
Makefile
Makefile.in
/src/httperf
/depcomp
/install-sh
/stamp-h1
/config.status
/config.h
/config.guess
/config.sub
/configure
/aclocal.m4
/compile
/ltmain.sh
/config.h.in
/config.log
/missing
/libtool
/autom4te.cache/07070100000001000081ED0000000000000000000000015FCD0002000001D1000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/AUTHORSDavid Mosberger
main sources
wsesslog crash fix
Martin Arlitt
SSL support
timer reset fix
Ted Bullock
compiler warning fixes
gnu auto tool build system
man page --wset typo fix regarding --uri
unallocated timer memory leak fix
Tai Jin
zero-length content fix
Stephane Eranian
wlog URI generator
Richard Carter
wsesslog workload generator
Andrew Hately
variable rates of period between sessions/requests
Adrian Chadd
port to libevent07070100000002000081ED0000000000000000000000015FCD000200003B1C000000000000000000000000000000000000002500000000httperf-0.9.0+git.20201206/COPYRIGHT GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
07070100000003000081ED0000000000000000000000015FCD000200007A4F000000000000000000000000000000000000002500000000httperf-0.9.0+git.20201206/ChangeLog2011-02-10 Ted Bullock <tbullock@comlore.com>
* Replaced bundled version of getopt with flavour from openbsd
(smaller code and more liberal redistribution license)
2011-02-07 Ted Bullock <tbullock@comlore.com>
* Adjusted help text
2009-12-16 Ted Bullock <tbullock@comlore.com>
* Remove libevent: Remove libevent (for now) from HEAD due to stability
2008-10-03 Ted Bullock <tbullock@canada.com>
* src/timer.c: Fix build on opensolaris with Sun Studio compiler
* src/lib/heap.c: Fix build on opensolaris with Sun Studio compiler
2008-07-14 Adrian Chadd <adrian@creative.net.au>
* Assorted .c and .h files: Use libevent for the communications backend
2008-04-11 Ted Bullock <tbullock@canada.com>
* configure.ac, src/Makefile.am: Make automake compilation optional,
use --enable-idleconn. This will require libevent.
2008-01-31 Ted Bullock <tbullock@canada.com>
* src/idleconn.c: Complete rewrite to use libevent backend; slight
performance improvement thanks to use of libevent notification system
2007-11-16 Ted Bullock <tbullock@canada.com>
* configure.ac: Enable libevent dependancy
* src/httperf.c: Print libevent version event notification mechanism
2007-11-15 Ted Bullock <tbullock@canada.com>
* autogen.sh: Remove in lieu of autoreconf -i
2007-09-23 Ted Bullock <tbullock@canada.com>
* httperf.c: Add a check to return value of timer_schedule
* timer.c: Fix a crash associated with rescheduling a timer
* lib/list.c: NEW Generic linked list data structure implementation
* lib/list.h: NEW Generic linked list data structure API
2007-09-18 Ted Bullock <tbullock@canada.com>
* timer.c: Remove the last of the old timer logic code and replace it
with simpler functionality
2007-09-11 Ted Bullock <tbullock@canada.com>
* timer.c and timer.h: Use new heap and queue data
structures to re-implement timers to use memory caching and
try to fix memory leak associated with timers not being freed
2007-09-11 Ted Bullock <tbullock@canada.com>
* lib/heap.c, lib/heap.h: New generic priority queue data structure
2007-07-15 Ted Bullock <tbullock@canada.com>
* lib/queue.c, lib/queue.h: New generic queue data structure
2007-07-15 Ted Bullock <tbullock@canada.com>
* lib/generic_types.h: New File - Migrate various generic types
out of httperf.h in preparation for a generic queue data structure
* httperf.h: Removed unneccessary #include commands to minimize
header inclusion blocks and duplicates
* *.c: Added #include statements to use new generic_types.h
2007-07-15 Ted Bullock <tbullock@canada.com>
* timer.c, timer.h, httperf.c: Fixed memory leak associated with timers not being
freed
2007-06-03 Ted Bullock <tbullock@canada.com>
* event.c and event.h: Moved to localevent.c and localevent.h for integration with
libevent (which has a default header of event.h)
2007-06-03 Ted Bullock <tbullock@canada.com>
* configure.ac: Add configuration to use libevent
2007-06-03 Ted Bullock <tbullock@canada.com>
* configure.ac: Remove unnecessary configuration cruft and configure httperf
to be built in c99 mode
2007-05-27 Ted Bullock <tbullock@canada.com>
* man/httperf.1: Fixed man page typo for --wset option (needed --uri, not --wset)
2007-05-27 Andrew Hateley <andrew.hateley@gmail.com>
* man/httperf.1: New documentation regarding the variable periodic rates
* src/httperf.c: New functionality for variable periodic rates
* src/httperf.h: ditto
* src/timer.c: ditto
2007-03-31 Ted Bullock <tbullock@canada.com>
* *.c and *.h: Adjusted license to explicitly list HP copyright as
Copyright 2000-2007 Hewlett-Packard Company
2007-02-07 Ted Bullock <tbullock@canada.com>
* AUTHORS: wsseslog crash fix was actually written by David Mosberger
2007-02-07 Ted Bullock <tbullock@canada.com>
* AUTHORS: Removed Durval Menezes from AUTHORS file since his fix
has been re-written by Theodore Bullock
2007-02-07 Ted Bullock <tbullock@canada.com>
* *.c and *.h: Slightly adjusted license to explicitly point out AUTHORS file
as official list of authors
2007-02-07 Ted Bullock <tbullock@canada.com>
* httperf.1: Added correction to man page to include a missing
--wsess identification
2007-01-26 Ted Bullock <tbullock@canada.com>
* getopt.c and getopt1.c: Re-Added the to the build process
httperf now compiles on Linux, OpenBSD, FreeBSD, Solaris, HP-UX 11i
with cc and gcc and (with cc and gcc)
2007-01-26 Ted Bullock <tbullock@canada.com>
* Makefile.am: Revised Makefile.am for src directories to explicitly include the
.h files to allow make distcheck to pass
2007-01-26 Ted Bullock <tbullock@canada.com>
* getopt.c and getopt1.c: Removed from build process
* httperf.c: Wrapped call to getopt.h in pre-processor checks
2007-01-25 Ted Bullock <tbullock@canada.com>
* wsesslog.c: changed a sprintf call to the more secure snprintf
* getopt.c: changed a sprintf call to the more secure snprintf
2007-01-20 Ted Bullock <tbullock@canada.com>
* autogen.sh: Included the shell script autogen.sh to compile the necessary
autotool components.
* autogen.sh: Removed redundant configuration files from CVS, regenerate these
with the autogen.sh script (recommend autoconf 2.60 and above)
* ttest.c: Removed the ttest.c file from CVS
2007-01-16 Ted Bullock <tbullock@canada.com>
* Build System: Updated build system to a more typical directory
layout and wrote missing makefile.am files for automake
* idleconn: Now installed along with the httperf executable
* ttest: Remove program from build
2007-01-02 Ted Bullock <tbullock@canada.com>
*License: Included openssl linking exception
2006-12-08 Ted Bullock <tbullock@canada.com>
* Bug Fixes: Added a number of fixes from the mailing lists
over the last 6 years.
* compiler warnings: Compiler warnings in core.c and wsesslog.c
* configuration: Fixes a problem with the "configure" setup
where the "--prefix=" option wasn't being obeyed
* zero length: Fixes bug in http.c (parse_header)with zero
length content
* Host Header: Identify machine by FQD rather than hostname
* Persistent Connections: Added Persistent connections to HTTP 1.0
requests
* Robust alloca.h: Robust way to handle the header file that
defines the alloca function <alloca.h> for linux and stdlib.h for
BSD and others
* Time Discrepancy: Fixed a discrepancy between the data produced
by httperf and the documentation in regards to timing.
* wsesslog Crash: Fixed a problem with the wsesslog crashing
httperf due to an unhandled session failure from a burdened server
2000-10-31 David Mosberger <davidm@hpl.hp.com>
* Version 0.8 released.
* core.c (do_recv) [!HAVE_SSL]: Avoid referncing param.use_ssl.
(do_send): Ditto.
* core.c (core_ssl_connect): Print error message if SSL connect
fails. ERR_print_errors_fp() may not print anything if there
error is due to, e.g., trying to connect to a non-existent port.
* httperf.c (main): Initialize port to -1. Once parameters have
been processed, port defaults to 443 if SSL is in use, 80
otherwise.
2000-10-30 David Mosberger <davidm@hpl.hp.com>
* httperf.man: Update man-page with new options (based on Martin's
descriptions.
* httperf.c (usage): Mention --port.
2000-10-27 David Mosberger <davidm@hpl.hp.com>
* httperf.man: Mention request summary in description of
--print-request.
* stat/print_reply.c (print_request): Print request header/content
summary line.
* httperf.c (usage): Bring in sync with implemented options
(Martin's patch).
(longopts): Order in alphabetical order (Martin's patch).
2000-10-25 David Mosberger <davidm@hpl.hp.com>
* stat/print_reply.c (flush_print_buf): New.
(Call_Private_Data): New.
(CALL_PRIVATE_DATA): New.
(call_private_data_offset): New.
(print_buf): Rewrite to support line-buffering and new output
format.
(call_destroyed): New.
(print_reply_hdr): New (based on Martin's original version).
(send_raw_data): Ditto.
(recv_raw_data): Ditto.
(recv_stop): Print reply summary (based on Martin's version).
(init): Update to support the new print options.
* httperf.c: Replace --print-replies/--print-request with more
general versions of these options.
* httperf.man: Add info on new --print-reply and --print-request
options.
2000-10-24 David Mosberger <davidm@hpl.hp.com>
* httperf.c (main): Initialize ssl_ctx.
(main): Use SSLv3_client_method() instead of SSLv23_client_method().
The latter doesn't work.
* httperf.h (ssl_ctx): New global variable.
* conn.h [HAVE_SSL]: Drop unnecessary includes of <openssl/rsa.h>,
<openssl/crypto.h>, <openssl/x509.h>, and <openssl/pem.h>. Drop
per-connect "ctx" member (it's global now).
* conn.c (conn_init): Create SSL connection info and set cipher
list (if necessary).
* core.c (core_ssl_connect): Don't create SSL connection info
here (done in conn.c now). Always tell SSL about the
file descriptor of the socket.
* sess.c (sess_deinit) [HAVE_SSL]: Free SSL info if it exists.
* gen/session.c (create_conn): If param.ssl_reuse is in effect,
re-use SSL session if we have one or update session info with
newly created SSL session.
* httperf.c [HAVE_SSL]: Include <openssl/rand.h>.
(main): Call RAND_seed() with a zero buffer.
2000-10-23 David Mosberger <davidm@hpl.hp.com>
* conn.c (conn_init): Don't initialize conn->ssl to zero---that
has been done by object_new() already.
* gen/session.c (create_conn): If reusing SSL session ids, create
SSL structure and save SSL info in session structure.
* sess.c (sess_deinit) [HAVE_SSL]: Free SSL session if it's
non-NULL.
* httperf.h: Declare "use_ssl" only if HAVE_SSL is defined.
Add ssl_no_reuse and ssl_cipher_list.
* httperf.c (struct longopts): Add --ssl-no-reuse and ssl-ciphers.
(main): Handle --ssl-cipher_list and --ssl-no-reuse options.
2000-10-19 David Mosberger <davidm@hpl.hp.com>
* core.c (core_ssl_connect): Bring debug messages in sync with
rest of httperf. If s->ssl exists already, skip right to
SSL_connect(). If SSL_connect() returns SSL_ERROR_WANT_READ or
SSL_ERROR_WANT_WRITE, mark the appropriate fd set as active and
return immediately to wait for more data. Use
ERR_print_errors_fp() to print SSL-related errors.
(core_loop): Check for S_CONNECTING state before doing anything
else.
* README: Document configuration option "--enable-debug".
* configure.in: Add support for --enable-debug.
2000-10-16 David Mosberger <davidm@hpl.hp.com>
* configure.in: Add AC_TYPE_LONG_LONG check (defines u_wide as
"unsigned long long" if the compiler can grok it, "unsigned long"
otherwise).
* aclocal.m4: New file.
* stat/basic.c: Include "config.h".
(struct basic): Change type of hdr_bytes_received,
reply_bytes_received, and footer_bytes_received from
size_t to u_wide.
(dump): Change type of total_size to u_wide.
2000-10-11 David Mosberger <davidm@hpl.hp.com>
* httperf.h (VERSION): Change to 0.8beta.
* configure.in: New file.
* Makefile.in: Ditto.
* stat/Makefile.in: Ditto.
* gen/Makefile.in: Ditto.
* lib/Makefile.in: Ditto.
2000-08-29 David Mosberger <davidm@hpl.hp.com>
* httperf.c (main): Call core_exit() on a SIGINT.
2000-08-21 David Mosberger <davidm@hpl.hp.com>
* core.c (core_init): Bound maximum number of open files to
FD_SETSIZE.
2000-04-25 David Mosberger <davidm@hpl.hp.com>
* httperf.c (main): Add a call to fpsetmask(0) to get non-finite
IEEE arithmetic to work on older versions of FreeBSD.
1998-12-21 David Mosberger <davidm@hpl.hp.com>
* Version 0.7 released.
* Makefile (install): New make target.
* httperf.h (VERSION): Define as "0.7".
* gen/wsesslog.c (parse_config): Remove blank in sscanf() format
since this caused problems on FreeBSD and NetBSD. This bug was
reported by Stephane Eranian.
1998-12-08 David Mosberger <david_mosberger@hp.com>
* httperf.c (struct longopts): Add option "method".
(usage): Document option --method.
(main): Handle --method.
* httperf.h (struct Cmdline_Params): New member "method".
* gen/misc.c: Renamed from add_header.c. This file now implements
both --add-header and --method.
1998-11-23 David Mosberger <david_mosberger@hp.com>
* httperf.c (longopts): New option "add-header".
(main): Handle --add-header.
* httperf.h (struct Cmdline_Params): New member
"additional_header".
* gen/add_header.c: New file.
1998-09-18 David Mosberger <davidm@hpl.hp.com>
* gen/session.c (call_done):
(conn_failed): Call create_conn() instead of sess_failure() in
case param.retry_on_failure is set and the connection has not been
successful (received at least one good response)
* gen/wsess.c (call_destroyed): Call sess_dec_ref() only if
session didn't fail.
1998-09-14 David Mosberger <davidm@hpl.hp.com>
* README: Replace "session" with "connection" and "call" with
"request" to reflect current terminology (reported by Christian
Petit). Also fixed various other typos and grammatical problems.
1998-09-11 David Mosberger <davidm@hpl.hp.com>
* gen/session.c (call_done): When param.retry_on_failure is set,
re-issue call instead of causing session to fail.
* httperf.c (usage): Add --retry-on-failure option.
(struct longopts): Ditto.
* httperf.man: Document --retry-on-failure option.
1998-07-23 David Mosberger <davidm@hpl.hp.com>
* stat/sess_stat.c: Use sess->failed instead of maintaining
private session-failure flag.
1998-07-21 David Mosberger <davidm@hpl.hp.com>
* sess.c (sess_failure): Don't signal EV_SESS_FAILED more than
once per session.
* core.c (do_send): Clear c->recvq_next. This is necessary now
because the same call may be issued multiple times (due to
connection failures). Hence there is no longer a guarantee that
c->recvq_next has been cleared by object_new().
(do_send): Rename s to conn and c to call.
1998-07-20 David Mosberger-Tang <David.Mosberger@acm.org>
* core.c (do_recv): Set s->state to S_REPLY_DONE when done with an
HTTP/1.0 response (mirrors code in http_process_reply_bytes()).
(do_recv): No need to check for nread == 0.
* stat/basic.c (init): Print basic.conn_lifetime_min only if
basic.num_lifetimes > 0.
* core.c (core_close): Fix typo (call -> all).
1998-07-08 David Mosberger <davidm@hpl.hp.com>
* core.c (do_send): Set s->state to S_REPLY_STATUS only if this is
the first send---we don't want to interfere with the parsing of an
in-progress reply.
1998-06-19 David Mosberger <davidm@hpl.hp.com>
* Version 0.6 released.
* gen/wsesslog.c (issue_calls): Limit `to_create' by the number
of calls that the session can queue.
* gen/session.c (session_max_qlen): New function.
(session_current_qlen): Ditto.
(max_qlen): New variable.
* httperf.man: Fix typos.
1998-06-18 David Mosberger <davidm@hpl.hp.com>
* gen/session.c (MAX_CONN): Up MAX_CONN to 16.
* object.c (object_new): panic() instead of assert(0) when
encountering an unknown object type.
* gen/wsesslog.c (user_think_time_expired): Remove stale comment.
* gen/sess_cookie.c (struct Call_Private_Data): New member cookie.
(call_recv_hdr): Remove padding bogosity.
(call_issue): Copy cookie from session to call object.
1998-06-17 David Mosberger <davidm@hpl.hp.com>
* timer.c (timer_cancel): Instead of an assertion failure, print a
message on stderr when timer_cancel(t) is called from within a
timeout handler t.
* Feedback from Dick Carter:
* gen/wsesslog.c (parse_config): Remove stale comment.
* httperf.h: Fix typo in comment for min_iat.
* httperf.c (usage): Fix typo in --period args.
* call.c (call_append_request_header): Fix typo in debug message.
1998-06-09 David Mosberger <davidm@hpl.hp.com>
* stat/print_reply.c: New file.
* stat/sess_stat.c: Ditto.
* stat/wsess_stat.c: Remove.
* stat/basic.c: Move stats macros into stats.h.
(RATE_INTERVAL): Remove.
(interval_start): Ditto.
(perf_sample): Rename from sample_rate().
(conn_fail): Count EPIPE as a `connection reset' error.
(init): Register perf_sample as handler for EV_PERF_SAMPLE.
Remove scheduling of performance sampling timer.
(dump): Print connection lifetime histogram only if verbose > 1.
Print average connection length in number of replies/connection.
* stat/stats.h: New file.
* stat/basic.c: Add copyright message.
* stat/Makefile (libstat.a): Mention sess_stat.o and
print_reply.o.
* object.c: New file.
* object.h: Ditto.
* sess.c: Ditto.
* sess.h: Ditto.
* httperf.h (VERSION): Up version number to 0.6.
(object_is_conn): Remove (now in object.h).
(object_is_call): Ditto.
(enum Object_Type): Ditto.
(struct Object): Ditto.
(object_type_size): Ditto.
(struct Cmdline_Params): New mebers max_piped, max_conns,
print_replies, and session_cookies.
* httperf.c (perf_sample_start): New variable to keep track of
when latest sample interval started.
(struct longopts): Add entries for max-connections,
max-piped-calls, print-replies, and session-cookies.
(usage): Update usage() message.
(perf_sample): New function.
(main): Remove uri_wsesslog generator. Add sess_stat stats
collector. Handle --max-connections, --max-piped, --print-replies
and --session-cookies. Start performance sampling timer.
* http.c (get_line): Use sizeof (s->line_buf) instead of
s->line_buf_size.
(parse_status_line): Do strcmp() to find out whether call
was a HEAD request. Ignore `100 Continue' replies.
* gen/wsesslog.h: Remove.
* gen/wsess.c: Modify to take advantage of session manager
(gen/session.c).
* gen/wsesslog.c: Modify to take advantage of session manager
and merge with uri_wsesslog.c.
* gen/uri_wlog.c (set_uri): Use call_set_uri().
* gen/uri_wset.c (set_uri): Ditto.
* gen/uri_fixed.c (uri_len): New variable.
(set_uri): Use call_set_uri().
(init): Initialize uri_len.
* gen/session.c: New file.
* gen/session.h: Ditto.
* gen/sess_cookie.c: New file (based on wsess.c).
* gen/conn_rate.c: File renamed from sess_rate.c.
* gen/call_seq.c: Modify to take advantage of reference counting.
* gen/Makefile (libgen.a): Remove uri_wsesslog (it's part of
wsesslog now).
* gen/uri_wsesslog.c: Remove.
* gen/uri_wsesslog.h: Ditto.
* event.h (Event_Type): New member EV_PERF_SAMPLE for performance
sampling (invoked once every 5 seconds). Rename EV_USER_NEW to
EV_SESS_NEW and EV_USER_DESTROYED to EV_SESS_DESTROYED. New
member EV_SESS_FAILED that is signalled when a session fails.
* core.c (do_send): Remove left-over assert()ion.
(do_send): Preserve io vector element that is being sent in
iov_saved. Call call_dec_ref() when the connection is closing.
(recv_done): Rename `c' to `call' and invoke call_dec_ref().
(do_recv): Call conn_failure() only if errno != EAGAIN (for Linux).
(core_send): Add `conn' argument. Fill in req.iov[IE_HOST] and
req.iov[IE_PROTL] only (rest has been filled in by call_set_* macros).
Call call_inc_ref() once the call is ready.
(core_close): Rename `s' to `conn'. Destroy pending calls calls
on the sendq and recvq by calling call_dec_ref(). Call conn_dec_ref()
at the end.
(core_loop): Obtain a reference to the connection object for the
duration of read & write processing.
* conn.h: Include <object.h>.
(MAX_HDR_LINE_LEN): New macro.
(struct Conn): Remove member first_owned and line_size. Replace
line_buf pointer with line_buf array.
(conn_init): Declare.
(conn_deinit): Ditto.
(conn_new): New macro.
(conn_inc_ref): Ditto.
(conn_dec_ref): Ditto.
* conn.c: Remove include of <errno.h> and <event.h>.
(conn_new): Remove.
(conn_destroy): Ditto.
(conn_init): New function.
(conn_deinit): Ditto.
* call.h: (enum HTTP_Method): Remove.
Include <object.h>.
(struct Call): Remove members loc_len, loc, uri_len, uri,
orig_host, extra_hdrs, contents_len, contents. New member:
iov_saved.
(call_init): Declare.
(call_deinit): Ditto.
(call_new): New macro.
(call_inc_ref): Ditto.
(call_dec_ref): Ditto.
(call_set_method): Ditto.
(call_set_location): Ditto.
(call_set_uri): Ditto.
(call_set_contents): Ditto.
* call.c (call_method_name): Remove.
(free_call_list): Remove.
(call_new): Remove.
(call_init): New function.
(call_deinit): Ditto.
(call_append_request_header): Fill req.iov[IE_FIRST_HEADER +
num_hdrs] instead of req.extra_hdrs[num_hdrs].
* Makefile (HTTPERF): Mention object and sess.
1998-05-29 David Mosberger <davidm@hpl.hp.com>
* Version 0.5 released.
* core.c (core_send): Fix setup of IE_METHOD and insert blank
character in IE_BLANK.
* call.h (enum IOV_Element): Add IE_BLANK.
1998-05-27 David Mosberger <davidm@hpl.hp.com>
* timer.c: Rename t_next to t_curr.
(timer_tick): Set t_next _after_ invoking the callback. This
allows the callback to cancel all other timers (except itself).
(timer_cancel): Add assertion to ensure that timer_cancel() is
not called from within the handler of a given timer.
* httperf.h (enum Dist_Type): New type.
(struct Rate_Info): Ditto.
(struct Cmdline_Params): Change type of RATE member from Time to
Rate_Info. New member WSESSLOG.
(panic): Declare.
* httperf.h (VERSION): Up version number to 0.5.
* httperf.c: Include <ctype.h> and <stdarg.h>. New options:
close-with-reset, period, wsesslog.
(usage): Add usage message for new options.
(panic): New function.
(main): Declare wsesslog, uri_wsesslog, and wsesslog_stat.
Initialize param.rate.dist with DETERMINISTIC. Handle --period
and --wsesslog options. Fix typo: user think time is 3rd, not 4th
parameter in wsess parameter list.
(main): Call timer_tick() after initializating the statistic
collectors and workload generators so our notion of the current
time is reasonably accurate.
* gen/wsess.c (req_create): Use call_append_request_header().
(start): Initialize rg_sess.rate with ¶m.rate.
* gen/uri_wsesslog.h: New file (by Dick Carter).
* gen/uri_wsesslog.c: Ditto.
* gen/wsesslog.h: Ditto.
* gen/wsesslog.c: Ditto.
* gen/uri_wlog.c: Drop inclusion of <stdarg.h>.
Move panic() from here to httperf.c.
* gen/sess_rate.c (start): Initialize rg.rate with ¶m.rate.
* gen/rate.h: New member next_interarrival_time. New member
xsubi. Replace PERIOD member with RATE member.
(rate_generator_stop): Drop second argument (RATE).
* gen/rate.c: Include <math.h>, <stdio.h>, and <stdlib.h>.
(next_arrival_time_det): New function.
(next_arrival_time_uniform): Ditto.
(next_arrival_time_exp): Ditto.
(tick): Invoke (*next_arrival_time)() to determine next delay.
(rate_generator_start): Initialize rg->xsubi based on
param.client.id. Initialize rg->next_interarrival_time based on
desired distribution.
* gen/Makefile (libgen.a): Mention wsesslog.o and uri_wsesslog.o
* core.c (core_connect): Don't rely on realloc(0, SIZE) doing the
right thing.
(core_connect): Clear newly allocated sd_to_conn elements after
allocating them.
(core_send): Use call_method_name. Copy in defined
c->req.extra_hdrs[i] iovecs. Copy in IE_CONTENT iovec.
* call.h: New value HM_LEN.
(MAX_EXTRA_HEADERS): New macro. Replace IE_HEADERS with
IE_FIRST_HEADER and IE_LAST_HEADER. Make extra_hdrs an array of
`struct iovec'. New member num_extra_hdrs.
(call_append_request_header): Declare new function.
* call.c: New array.
(call_append_request_header): New function.
1998-05-26 David Mosberger <davidm@hpl.hp.com>
* COPYRIGHT: Add 1998 as copyright year.
* stat/basic.c (dump): If no connections have been created, print
0.0 for minimm connection lifetime. This avoids a core-dump under
FreeBSD, which seems to have a problem printing IEEE infinity.
1998-05-22 David Mosberger <davidm@hpl.hp.com>
* call.c (method_name): New variable.
1998-04-27 David Mosberger <davidm@hpl.hp.com>
* core.c (do_recv): Ignore EAGAIN errors---Linux 2.0.32 sometimes
returns EAGAIN on a read() if a previous write() resulted in
EAGAIN.
* AUTHORS: New file.
1998-03-24 David Mosberger <davidm@hpl.hp.com>
* gen/wsess.c (call_done): Also fall back to non-persistent
session if c->req.version < HTTP/1.1.
1998-03-18 David Mosberger <davidm@hpl.hp.com>
* httperf.c (usage): Fix typo: --recv-buffer -> --send-buffer.
* gen/wsess.c (dump): Rename "Sess" to "Session" in statistics
output.
* core.c (core_connect): Turn on closing with RESET only when
param.close_with_reset is set.
1998-03-17 David Mosberger <davidm@hpl.hp.com>
* stat/basic.c (dump): In "Total" line, rename "call" to "req".
* Version 0.41 released.
* core.c: Include <sys/resource.h> after <sys/types.h> to get it
to compile under FreeBSD (which seems to have a broken libc).
* stat/basic.c (dump): Complete renaming of "session" to "call".
Rename "Call" to "Request".
1998-03-16 David Mosberger <davidm@hpl.hp.com>
* Version 0.4 released.
* httperf.h (VERSION): Change version number to 0.4.
* httperf.c: (longopts): Add --http-version.
(main): Initialize param.http_version to HTTP/1. Support parsing
of --http-version option. Echo --http-version option if version
other than 1.1 is selected.
* httperf.h (struct Cmdline_Params): New member HTTP_VERSION.
* core.c (core_send): Support generation of HTTP/1.0 requests.
* Patches by Stephane Eranian:
* httperf.man: Mention --wlog parameter.
* Makefile (clean): Use $(MAKE) instead of make.
1998-03-12 David Mosberger <davidm@hpl.hp.com>
* httperf.c: Renamed Session to Conn to avoid confusion between
user sessions and TCP connections.
1998-03-02 David Mosberger <davidm@hpl.hp.com>
* gen/wsess.c (user_destroy): Signal EV_USER_DESTROYED.
(user_create): Signal EV_USER_NEW.
(start): Register rate-generator with event EV_USER_DESTROYED
instead of EV_SESSION_DESTROYED.
* event.h: New events EV_USER_NEW and EV_USER_DESTROYED.
* event.c: Ditto.
1998-02-27 David Mosberger <davidm@hpl.hp.com>
* session.h: Remove unused first_call member.
1998-02-26 David Mosberger <davidm@hpl.hp.com>
* gen/wsess.c (call_recv_hdr): Always check for Connection: and
Set-Cookie: headers (not just for first call).
(req_create): Modify to support bursts.
(user_connect): Ditto.
(sess_destroyed): Ditto.
(call_destroyed): Ditto.
1998-02-25 David Mosberger <davidm@hpl.hp.com>
* core.c (lffs): Fix lffs() so it returns 0 when no bits at all
are set in a 64 bit long.
(set_active): Set recvq timeout if timeout==0.0 (the old code failed
to timeout connections that were waiting for replies).
1998-02-23 David Mosberger <davidm@hpl.hp.com>
* Version 0.3 released.
* timer.c (timer_now_forced): New function.
* httperf.h (Cmdline_params): New member burst_len.
* httperf.c: New option --burst-length. At present, only
gen/call_seq.c supports this (gen/wsess does not).
* gen/wsess.c (struct User): New members: found_close_hdr,
conn_start, call_start, and cookie.
(req_create): Insert cookie as extra header if there is one.
(call_recv_hdr): New function. Looks for "Connection: close" and
"Set-Cookie:" headers.
* gen/call_seq.c (Sess_Private_Data): New member "num_finished".
(issue_calls): Add support for --burst-len parameter.
(call_destroyed): Ditto.
* core.c (SYSCALL): Call timer_now_forced() instead of timer_now().
(core_connect): Keep track of longest back-to-back burst.
(core_connect): Set SO_LINGER option so that TCP RST is sent when
closing the socket. This avoids the client melting down on
HP-UX 10.20 due to a large number of TCP connections in TIME_WAIT
state (HP-UX is broken as far as bind()/connect() is concerned when
there are many TCP control blocks).
(core_exit): Print maximum connect burst length.
* core.c (max_burst_len): New variable to keep track of longest
back-to-back burst generated (large bursts are usually a sign of
the client being overloaded).
1998-02-13 David Mosberger <davidm@hpl.hp.com>
* stat/basic.c (sess_fail): Print one unexpected error number per
execution, independent of DBG.
1998-02-06 David Mosberger <davidm@hpl.hp.com>
* core.c (do_send): Maintain sendq_tail & recvq_tail.
(core_send): Ditto.
(recv_done): Ditto.
* session.h (Session): New members SENDQ_TAIL and RECVQ_TAIL.
* core.c (set_active): Set watchdog timer to the minimum of the
timeout of the request being sent and that being received (if such
requests exist).
(do_send): Set c->timeout.
(core_connect): Ditto.
(core_send): Ditto.
* call.h: Replace (unused) WATCHDOG member with TIMEOUT.
* timer.c (timer_schedule): Handle negative delays gracefully.
1998-01-28 David Mosberger <davidm@hpl.hp.com>
* Version 0.2 released to SixFlags team.
1998-01-26 David Mosberger <davidm@hpl.hp.com>
* gen/call_seq.c (sess_closed): New function.
(init): Register sess_closed() so we notice when session closes
prematurely.
1998-01-21 David Mosberger <davidm@hpl.hp.com>
* httperf.c (main): Don't quit after printing version
information---it's useful to run a test and have this info as
well.
1998-01-14 David Mosberger <davidm@hpl.hp.com>
* stat/basic.c (dump): Add num_sock_reset to total error count.
1998-01-12 David Mosberger <davidm@hpl.hp.com>
* stat/basic.c (struct basic): New member num_sock_reset.
(sess_fail): Increment num_sock_reset for ECONNRESET.
(dump): Print num_sock_reset.
* core.c (do_recv): Call session_failure() when reading < 0 bytes.
* stat/basic.c (sess_fail): Print error number when getting an
unexpected errno and DBG > 0.
1998-01-09 David Mosberger <davidm@hpl.hp.com>
* httperf.c (main): Print only one newline (second newline is
printed by stat collectors).
* stat/basic.c (dump): Print session lifetime histogram first
(so it goes right after the rate output).
1998-01-08 David Mosberger <davidm@hpl.hp.com>
* core.c (port_get): Recode so port numbers are returned in
strictly increasing order (until a wraparound after 65535 occurs).
This avoids problems with NT 4.0 which disallows reusing the port
pair of a properly shut down TCP connection (which creates
problems when testing an NT server with clients running on HP-UX
or similar platforms).
1998-01-07 David Mosberger <davidm@hpl.hp.com>
* gen/sess_rate.c: Adapt to use rate.c.
* timer.c (timer_schedule): Adjust "delay" if realtime has grown
bigger than next_tick (meaning that "spoke" is behind).
* call.c (call_destroy): prev wasn't updated in the search loop.
1998-01-06 David Mosberger <davidm@hpl.hp.com>
* stat/basic.c (dump): Print minmum rate as zero if there are no
rate samples.
1997-12-23 David Mosberger <davidm@hpl.hp.com>
* stat/basic.c (struct basic): Add members num_reply_rates,
reply_rate_sum, reply_rate_sum2, reply_rate_min, and
reply_rate_max.
(interval_start, num_replies): New vars.
(sample_rate): New function to sample reply rate.
(init): Schedule rate sampler.
(dump): Print min, avg, max, and stddev of sampled reply rate.
* httperf.c: Add --client and --think-timeout options.
1997-12-17 David Mosberger <davidm@hpl.hp.com>
* http.c (parse_data): Return TRUE to indicate that all bytes
(of present chunk) have been received (instead of setting
state to S_REPLY_DONE).
* httperf.h (ALIGN): Fix macro (need AND with _complement_...).
* http.c (xfer_chunked): Fix typo: check for content_bytes >=
content_length (not vice versa).
* uri_wset.c (set_uri): Fix uri_len computation.
* sess_rate.c (destroyed): Fix typo: test for param.rate <= 0 (not
<0).
07070100000004000081ED0000000000000000000000015FCD000200000045000000000000000000000000000000000000002700000000httperf-0.9.0+git.20201206/Makefile.amAUTOMAKE_OPTIONS = foreign
SUBDIRS = src man
ACLOCAL_AMFLAGS = -I m4
07070100000005000081ED0000000000000000000000015FCD000200000B01000000000000000000000000000000000000002000000000httperf-0.9.0+git.20201206/NEWS* New in version 0.9.1:
** timer re-write to reduce memory and fix memory leaks
** Generic data structures and implementations (heap, queue, linked list)
** New options (see man-page for details):
--period=vT1,D1,T2,D2...Tn,Dn
** Complete re-write of idleconn.c to use libevent notification system
** idleconn is now optional (--enable-idleconn)
* New in version 0.9.0:
** Re-Factored build system now builds on the following platforms
HP-UX 11i (64-bit PA-RISC and IA-64)
Red Hat Enterprise Linux AS (AMD64 and IA-64)
SUSE Linux 10.1 (i386)
openSUSE 10.2 (i386)
OpenBSD 4.0 (i386)
FreeBSD 6.0 (AMD64)
Solaris 8 (UltraSparc 64-bit)
* New in version 0.8.1:
** Numerous bug fixes. See ChangeLog for details
* New in version 0.8:
** httperf is now released under the GNU General Public License (GPL).
** Preliminary support for SSL (Secure Socket Layer). See README for details.
** New options (see man-page for details):
--print-reply (replaced --print-replies)
--print-request
--ssl
--ssl-ciphers
--ssl-no-reuse
* New in version 0.7:
** New options (see man-page for details):
--add-header
--method
--retry-on-failure
** Bug fixes
- fixed some segfaults in the session workload generator
- specifying option --session-cookie when not using a session
workload generator now prints an error message instead of
core dumping
* New in version 0.6:
** New options (see man-page for details):
--max-connections
--max-piped-calls
--print-replies
--session-cookies
** Cookie support now must be requested explicitly when using a session-based
workload generator. To do this, specify option --session-cookie.
* New in version 0.5:
** Normal connection closing is the default again. To request closing TCP
connections with a RESET, specify option --close-with-reset.
** --wsesslog option added to support log-file based session
specification (contributed by Dick Carter).
** --period option added to allow a more flexible way to specify
session/connection interarrival time. Unlike the --rate
argument, this allows deterministic (fixed), uniform, and
exponentially distributed interarrival times (contributed by Dick
Carter).
** Various bug fixes (see ChangeLog for details).
* New in version 0.41:
** In basic statistic, rename "call" to "request".
* New in version 0.4:
** Option --http-version can be used to select the HTTP protocol version
used in sending requests. 1.0 and 1.0 are the only allowed values
for this option at this point.
** What used to be called a "session" is now called a "connection". This
reduces confusion between TCP connections and user sessions.
** Stephane's log-file based URL generated has been added.
** The session workload generator now supports the --burst-length
parameter to generate bursty session.
07070100000006000081ED0000000000000000000000015FCD000200004919000000000000000000000000000000000000002500000000httperf-0.9.0+git.20201206/README.md
# httperf
httperf is a tool for measuring web server performance. It provides a flexible facility for generating various HTTP workloads and for measuring server performance.
The focus of httperf is not on implementing one particular benchmark but on providing a robust, high-performance tool that facilitates the construction of both micro- and macro-level benchmarks. The three distinguishing characteristics of httperf are its robustness, which includes the ability to generate and sustain server overload, support for the HTTP/1.1 and SSL protocols, and its extensibility to new workload generators and performance measurements.
## Building httperf
This release of httperf is using the standard GNU configuration
mechanism. The following steps can be used to build it:
In this example, SRCDIR refers to the httperf source directory. The
last step may have to be executed as "root".
First, some tools which are required for the build process need to be installed.
$ sudo apt install automake libtool
Then, run the following steps in order to build. Note that some of these might have to be executed as "root", i.e., with sudo.
$ autoconf
$ libtoolize --force
$ autoreconf -i
$ automake
$ ./configure
$ make
$ sudo make install
This step may need to be run as root:
make install
Since httperf 0.9.1, the the idleconn program is no longer built by
default. Using the configure option --enable-idleconn will instruct
the build system to compile the tool.
To build httperf with debug support turned on, invoke configure with
option "--enable-debug".
By default, the httperf binary is installed in /usr/local/bin/httperf
and the man-page is installed in /usr/local/man/man1/httperf. You can
change these defaults by passing appropriate options to the
"configure" script. See "configure --help" for details.
This release of httperf has preliminary SSL support. To enable it,
you need to have OpenSSL (http://www.openssl.org/) already installed
on your system. The configure script assumes that the OpenSSH header
files and libraries can be found in standard locations (e.g.,
/usr/include and /usr/lib). If the files are in a different place,
you need to tell the configure script where to find them. This can be
done by setting environment variables CPPFLAGS and LDFLAGS before
invoking "configure". For example, if the SSL header files are
installed in /usr/local/ssl/include and the SSL libraries are
installed in /usr/local/ssl/lib, then the environment variables should
be set like this:
CPPFLAGS="-I/usr/local/ssl/include"
LDFLAGS="-L/usr/local/ssl/lib"
With these settings in place, "configure" can be invoked as usual and
SSL should now be found. If SSL has been detected, the following
three checks should be answered with "yes":
checking for main in -lcrypto... yes
checking for SSL_version in -lssl... yes
:
checking for openssl/ssl.h... yes
Note: you may have to delete "config.cache" to ensure that "configure"
re-evaluates those checks after changing the settings of the
environment variables.
WARNING:
httperf uses a deterministic seed for the random number
generator used by SSL. Thus, the SSL encrypted data is
likely to be easy to crack. In other words, do not assume
that SSL data transferred when using httperf is (well)
encrypted!
This release of httperf has been tested under the following operating systems:
HP-UX 11i (64-bit PA-RISC and IA-64)
Red Hat Enterprise Linux AS (AMD64 and IA-64)
SUSE Linux 10.1 (i386)
openSUSE 10.2 (i386)
OpenBSD 4.0 (i386)
FreeBSD 6.0 (AMD64)
Solaris 8 (UltraSparc 64-bit)
It should be straight-forward to build httperf on other platforms, please report
any build problems to the mailing list along with the platform specifications.
## Mailing list
A mailing list has been set up to encourage discussions among the
httperf user community. This list is managed by majordomo. To
subscribe to the list, send a mail containing the body:
subscribe httperf
to majordomo@linux.hpl.hp.com. To post an article to the list, send
it directly to httperf@linux.hpl.hp.com.
## Running httperf
IMPORTANT: It is crucial to run just one copy of httperf per client
machine. httperf sucks up all available CPU time on a machine. It is
therefore important not to run any other (CPU-intensive) tasks on a
client machine while httperf is running. httperf is a CPU hog to
ensure that it can generate the desired workload with good accuracy,
so do not try to change this without fully understanding what the
issues are.
### Examples
The simplest way to invoke httperf is with a command line of the form:
> httperf --server wailua --port 6800
This command results in httperf attempting to make one request for URL
http://wailua:6800/. After the reply is received, performance
statistics will be printed and the client exits (the statistics are
explained below).
A list of all available options can be obtained by specifying the
--help option (all option names can be abbreviated as long as they
remain unambiguous).
A more realistic test case might be to issue 100 HTTP requests at a
rate of 10 requests per second. This can be achieved by additionally
specifying the --num-conns and --rate options. When specifying the
--rate option, it's generally a good idea to also specify a timeout
value using the --timeout option. In the example below, a timeout of
one second is specified (the ramification of this option will be
explained later):
> httperf --server wailua --port 6800 --num-conns 100 --rate 10 --timeout 1
The performance statistics printed by httperf at the end of the test
might look like this:
Total: connections 100 requests 100 replies 100 test-duration 9.905 s
Connection rate: 10.1 conn/s (99.1 ms/conn, <=1 concurrent connections)
Connection time [ms]: min 4.6 avg 5.6 max 19.9 median 4.5 stddev 2.0
Connection time [ms]: connect 1.4
Connection length [replies/conn]: 1.000
Request rate: 10.1 req/s (99.1 ms/req)
Request size [B]: 57.0
Reply rate [replies/s]: min 10.0 avg 10.0 max 10.0 stddev 0.0 (1 samples)
Reply time [ms]: response 4.1 transfer 0.0
Reply size [B]: header 219.0 content 204.0 footer 0.0 (total 423.0)
Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
CPU time [s]: user 2.71 system 7.08 (user 27.4% system 71.5% total 98.8%)
Net I/O: 4.7 KB/s (0.0*10^6 bps)
Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
There are six groups of statistics: overall results ("Total"),
connection related results ("Connection"), results relating to the
issuing of HTTP requests ("Request"), results relating to the replies
received from the server ("Reply"), miscellaneous results relating to
the CPU time and network bandwidth used, and, finally, a summary of
errors encountered ("Errors"). Let's discuss each in turn:
## "Total" Results
The "Total" line summarizes how many TCP connections were initiated by
the client, how many requests it sent, how many replies it received,
and what the total test duration was. The line below shows that 100
connections were initiated, 100 requests were performed and 100
replies were received. It also shows that total test-duration was
9.905 seconds meaning that the average request rate was almost exactly
10 request per second.
Total: connections 100 requests 100 replies 100 test-duration 9.905 s
## "Connection" Results
These results convey information related to the TCP connections that
are used to communicate with the web server.
Specifically, the line below show that new connections were initiated
at a rate of 10.1 connections per second. This rate corresponds to a
period of 99.1 milliseconds per connection. Finally, the last number
shows that at most one connection was open to the server at any given
time.
Connection rate: 10.1 conn/s (99.1 ms/conn, <=1 concurrent connections)
The next line in the output gives lifetime statistics for successful
connections. The lifetime of a connection is the time between a TCP
connection was initiated and the time the connection was closed. A
connection is considered successful if it had at least one request
that resulted in a reply from the server. The line shown below
indicates that the minimum ("min") connection lifetime was 4.6
milliseconds, the average ("avg") lifetime was 5.6 milliseconds, the
maximum ("max") was 19.9 milliseconds, the median ("median") lifetime
was 4.5 milliseconds, and that the standard deviation of the lifetimes
was 2.0 milliseconds.
Connection time [ms]: min 4.6 avg 5.6 max 19.9 median 4.5 stddev 2.0
To compute the median time, httperf collects a histogram of connection
lifetimes. The granularity of this histogram is currently 1
milliseconds and the maximum connection lifetime that can be
accommodated with the histogram is 100 seconds (these numbers can be
changed by editing macros BIN_WIDTH and MAX_LIFETIME in stat/basic.c).
This implies that the granularity of the median time is 1 millisecond
and that at least 50% of the lifetime samples must have a lifetime of
less than 100 seconds.
The next statistic in this section is the average time it took to
establish a TCP connection to the server (all successful TCP
connections establishments are counted, even connections that may have
failed eventually). The line below shows that, on average, it took
1.4 milliseconds to establish a connection.
Connection time [ms]: connect 1.4
The final line in this section gives the average number of replies
that were received per connection. With regular HTTP/1.0, this value
is at most 1.0 (when there are no failures), but with HTTP Keep-Alives
or HTTP/1.1 persistent connections, this value can be arbitrarily
high, indicating that the same connection was used to receive multiple
responses.
Connection length [replies/conn]: 1.000
## "Request" Results
The first line in the "Request"-related results give the rate at which
HTTP requests were issued and the period-length that the rate
corresponds to. In the example below, the request rate was 10.1
requests per second, which corresponds to 99.1 milliseconds per
request.
Request rate: 10.1 req/s (99.1 ms/req)
As long as no persistent connections are employed, the "Request"
results are typically very similar or identical to the "Connection"
results. However, when persistent connections are used, several
requests can be issued on a single connection in which case the
results would be different.
The next line gives the average size of the HTTP request in bytes. In
the line show below, the average request size was 57 bytes.
Request size [B]: 57.0
## "Reply" Results
For simple measurements, the section with the "Reply" results is
probably the most interesting one. The first line gives statistics on
the reply rate:
Reply rate [replies/s]: min 10.0 avg 10.0 max 10.0 stddev 0.0 (1 samples)
The line above indicates that the minimum ("min"), average ("avg"),
and maximum ("max") reply rate was ten replies per second. Given
these numbers, the standard deviation is, of course, zero. The last
number shows that only one reply rate sample was acquired. The
present version of httperf collects one rate sample about once every
five seconds. To obtain a meaningful standard deviation, it is
recommended to run each test long enough so at least thirty samples
are obtained---this would correspond to a test duration of at least
150 seconds, or two and a half minutes.
The next line gives information on how long it took for the server to
respond and how long it took to receive the reply. The line below
shows that it took 4.1 milliseconds between sending the first byte of
the request and receiving the first byte of the reply. The time to
"transfer", or read, the reply was too short to be measured, so it
shows up as zero (as we'll see below, the entire reply fit into a
single TCP segment and that's why the transfer time was measured as
zero).
Reply time [ms]: response 4.1 transfer 0.0
Next follow some statistics on the size of the reply---all numbers are
reported in bytes. Specifically, the average length of reply headers,
the average length of the content, and the average length of reply
footers are given (HTTP/1.1 uses footers to realize the "chunked"
transfer encoding). For convenience, the average total number of
bytes in the replies is also given. In the example below, the average
header length ("header") was 219 bytes, the average content length
("content") was 204 bytes, and there were no footers ("footer"),
yielding a total reply length of 423 bytes on average.
Reply size [B]: header 219.0 content 204.0 footer 0.0 (total 423.0)
The final piece in this section is a histogram on the status codes
received in the replies. The example below shows that all 100 replies
were "successful" replies as they contained a status code of 200
(presumably):
Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
## Miscellaneous Results
This section starts with a summary of the CPU time the client
consumed. The line below shows that 2.71 seconds were spent executing
in user mode ("user"), 7.08 seconds were spent executing in system
mode ("system") and that this corresponds to 27.4% user mode execution
and 71.5% system execution. The total utilization was almost exactly
100%, which is expected given that httperf is a CPU hog:
CPU time [s]: user 2.71 system 7.08 (user 27.4% system 71.5% total 98.8%)
Note that any time the total CPU utilization is significantly less
than 100%, some other processes must have been running on the client
machine while httperf was executing. This makes it likely that the
results are "polluted" and the test should be rerun.
The next line gives the average network throughput in kilobytes per
second (where a kilobyte is 1024 bytes) and in megabits per second
(where a megabit is 10^6 bit). The line below shows an average
network bandwidth of about 4.7 kilobyte per second. The megabit per
second number is zero due to rounding errors.
Net I/O: 4.7 KB/s (0.0*10^6 bps)
The network bandwidth is computed from the number of bytes sent and
received on TCP connections. This means that it accounts for the
network payload only (i.e., it doesn't account for protocol headers)
and does not take into account retransmissions that may occur at the
TCP level.
## "Errors"
The final section contains statistics on the errors that occurred
during the test. The "total" figure shows the total number of errors
that occurred. The two lines below show that in our example run there
were no errors:
Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
The meaning of each error is described below:
total:
The sum of all following error counts.
client-timo:
Each time a request is made to the server, a watchdog timer
is started. If no (partial) response is received by the time
the watchdog timer expires, httperf times out that request
a increments this error counter. This is the most common error
when driving a server into overload.
socket-timo
The number of times a TCP connection failed with a
socket-level time out (ETIMEDOUT).
connrefused
The number of times a TCP connection attempt failed with
a "connection refused by server" error (ECONNREFUSED).
connreset
The number of times a TCP connection failed due to a reset
(close) by the server.
fd-unavail
The number of times the httperf client was out of file
descriptors. Whenever this count is bigger than
zero, the test results are meaning less because the client
was overloaded (see discussion on setting --timeout below).
addrunavail
The number of times the client was out of TCP port numbers
(EADDRNOTAVAIL). This error should never occur. If it
does, the results should be discarded.
ftab-full
The number of times the system's file descriptor table
was full. Again, this error should never occur. If it
does, the results should be discarded.
other
The number of times other errors occurred. Whenever this
occurs, it is necessary to track down the actual error
reason. This can be done by compiling httperf with
debug support and specifying option --debug 1.
## Selecting appropriate timeout values
Since the client machine has only a limited set of resource available,
it cannot sustain arbitrarily high HTTP request rates. One limit is
that there are only roughly 60,000 TCP port numbers that can be in use
at any given time. Since, on HP-UX, it takes one minute for a TCP
connection to be fully closed (leave the TIME_WAIT state), the maximum
rate a client can sustain is about 1,000 requests per second.
The actual sustainable rate is typically lower than this because
before running out of TCP ports, a client is likely to run out of file
descriptors (one file descriptor is required per open TCP connection).
By default, HP-UX 10.20 allows 1024 file descriptors per process.
Without a watchdog timer, httperf could potentially quickly use up all
available file descriptors, at which point it could not induce any new
load on the server (this would primarily happen when the server is
overloaded). To avoid this problem, httperf requires that the web
server must respond within the time specified by option --timeout. If
it does not respond within that time, the client considers the
connection to be "dead" and closes it (and increases the "client-timo"
error count). The only exception to this rule is that after sending a
request, httperf allows the server to take some additional time before
it starts responding (to accommodate HTTP requests that take a long
time to complete on the server). This additional time is called the
"server think time" and can be specified by option --think-timeout.
By default, this additional think time is zero, so by default the
server has to be able to respond within the time allowed by the
--timeout option.
In practice, we found that with a --timeout value of 1 second, an HP
9000/735 machine running HP-UX 10.20 can sustain a rate of about 700
connections per second before it starts to run out of file descriptor
(the exact rate depends, of course, on a number of factors). To
achieve web server loads bigger than that, it is necessary to employ
several independent machines, each running one copy of httperf. A
timeout of one second effectively means that "slow" connections will
typically timeout before TCP even gets a chance to retransmit (the
initial retransmission timeout is on the order of 3 seconds). This is
usually OK, except that one should keep in mind that it has the effect
of truncating the connection life time distribution.
07070100000007000081ED0000000000000000000000015FCD000200000647000000000000000000000000000000000000002000000000httperf-0.9.0+git.20201206/TODOSome ideas (contributions/patches welcome):
- port to libevent to improve scalability and deal with the file descriptor cap
- Add option to output results as rdf (xml), cvs or default
- Add ability to read entire POST and GET messages from logs and send them
- wsesspage: don't fetch same object more than once (assume the existence
of a cache)---this avoids trouble with recursive pages
- make httperf easier to use; some ideas:
o Make httperf into a network daemon that is controlled by
an httperf frontend. This would allow running tests with
a single command line even when multiple clients are involved.
The performance results should be reported both on a
per-client basis and in a summarized form reporting overall
server behavior.
o Provide (default) scripts to run certain benchmarks.
o Provide (default) scripts to produce performance graphs.
- use cycle registers to get time on CPUs that have such registers
(IA-64, PA-RISC, x86, Alpha, at least)
- randomize the uri's generated by --wset so we can defeat file prefetching
that the server (or the server's OS) may attempt to do
Done:
+ Specifying --session-cookie without specifying a session workload causes
httperf to core-dump (reported by Dick Carter, 10/13/98)
+ elevate `Session' to same level as Call and Connection
+ sample session throughput when using sessions (not just rate throughput)
+ integrate Dick's wsesslog
+ write man-page
+ chunked replies fail after the first reply (at least on HP-UX)
+ core.c does not fully support pipelined requests yet; would require
changes to the watchdog management
07070100000008000081ED0000000000000000000000015FCD000200000A45000000000000000000000000000000000000002800000000httperf-0.9.0+git.20201206/configure.ac# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.60)
AC_INIT(httperf, 0.9.1, httperf@linux.hpl.hp.com)
AC_CONFIG_SRCDIR([src/httperf.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE(httperf, 0.9.1)
# Checks for programs.
AC_GNU_SOURCE
AC_PROG_CC([cc gcc])
AC_PROG_CC_C99
AC_PROG_INSTALL
AC_PROG_LIBTOOL
# Are we building the idleconn program?
AC_ARG_ENABLE([idleconn],
AS_HELP_STRING([--enable-idleconn],
[Build the idleconn program (default=no), requires libevent]),
[enable_idleconn=$enableval],
[enable_idleconn=no])
if test "$enable_idleconn" = "yes"; then
AC_CHECK_LIB([event],
[event_init],
,
[AC_MSG_ERROR([libevent is required to build idleconn])])
fi
AM_CONDITIONAL(IDLECONN,
[test "x$enable_idleconn" = xyes])
# Checks for libraries.
AC_CHECK_LIB(m, sqrt)
AC_CHECK_LIB(crypto, main)
AC_CHECK_LIB(ssl, SSL_version, , AC_MSG_WARN([SSL Disabled]) )
# The following checks are for solaris and its ilk
AC_SEARCH_LIBS([getsockopt], [socket])
AC_SEARCH_LIBS([socket], [socket nsl])
AC_SEARCH_LIBS([gethostbyname], [socket nsl])
AC_SEARCH_LIBS([inet_aton], [resolv])
# Checks for header files.
AC_FUNC_ALLOCA
AC_HEADER_TIME
AC_CHECK_HEADERS([openssl/ssl.h getopt.h])
if test "$ac_cv_header_openssl_ssl_h" = "yes" \
-a "$ac_cv_lib_ssl_SSL_version" = "yes" \
-a "$ac_cv_lib_crypto_main" = "yes"; then
CFLAGS="${CFLAGS} -DHAVE_SSL"
LDFLAGS="${LDFLAGS}"
fi
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_CHECK_TYPE(u_char, unsigned char)
AC_CHECK_TYPE(u_short, unsigned short)
AC_CHECK_TYPE(u_int, unsigned int)
AC_CHECK_TYPE(u_long, unsigned long)
# Check for unsigned long long int (stat/basic.c)
AC_TYPE_UNSIGNED_LONG_LONG_INT
if test "$ac_cv_type_unsigned_long_long_int" != "yes" ; then
AC_CHECK_TYPE(u_wide, unsigned long)
else
AC_CHECK_TYPE(u_wide, unsigned long long)
fi
# Checks for library functions.
AC_FUNC_MMAP
AC_FUNC_SELECT_ARGTYPES
AC_TYPE_SIGNAL
AC_FUNC_STRTOD
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([getopt_long])
# Turn on Debug if necessary
AC_ARG_ENABLE(debug,
[ --enable-debug enable debug support])
if test "$enable_debug" = yes; then
CFLAGS="${CFLAGS} -DDEBUG"
fi
AC_ARG_WITH(epoll, AS_HELP_STRING([--with-epoll], [use epoll if available]))
AS_IF([test "$with_epoll" != "no"],
AC_CHECK_FUNC(epoll_create,
AC_DEFINE([HAVE_EPOLL], 1, [#undef HAVE_EPOLL])))
AC_OUTPUT(Makefile man/Makefile src/stat/Makefile src/lib/Makefile src/gen/Makefile src/Makefile)
07070100000009000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/contrib0707010000000A000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000003400000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage0707010000000B000081ED0000000000000000000000015FCD000200000444000000000000000000000000000000000000004100000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage/genallman.sh#!/bin/bash
# Generate several manpages at the same time.
# Copyright (C) 2014-2015 Joao Eriberto Mota Filho <eriberto@debian.org>
# v0.3, available at https://github.com/eribertomota/genallman
#
# You can use this code in the same terms of the BSD-3-clause license or,
# optionally, in the same terms of the license used in debian/ directory
# when packaging for Debian or similar.
#
# This script uses txt2man. You need 2 files: program_name.txt and
# program_name.header.
#
# The program_name.header must be use this structure:
#
# .TH <program_name> "<manpage_level>" "<date>" "<program_name_upper_case> <program_version>" "<program_description>"
#
# Example:
#
# .TH mac-robber "1" "May 2013" "MAC-ROBBER 1.02" "collects data about allocated files in mounted filesystems"
[ -f /usr/bin/txt2man ] || { echo "ERROR: txt2man not found."; exit; }
for NAME in $(ls | grep header | cut -d'.' -f1)
do
LEVEL=$(cat $NAME.header | cut -d" " -f3 | tr -d '"')
cat $NAME.header > $NAME.$LEVEL
txt2man $NAME.txt | grep -v '^.TH ' >> $NAME.$LEVEL
echo "Generated $NAME.$LEVEL."
done
0707010000000C000081A40000000000000000000000015FCD00020000005F000000000000000000000000000000000000004400000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage/idleconn.header.TH idleconn "1" "Mar 2016" "IDLECONN 0.9.0" "tool for opening any number of idle connections"
0707010000000D000081A40000000000000000000000015FCD00020000050B000000000000000000000000000000000000004100000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage/idleconn.txtNAME
idleconn - tool for opening any number of idle connections
SYNOPSIS
idleconn <server> <port> <numidle>
DESCRIPTION
idleconn is part of httperf suite and is useful to simulate a large number
of concurrent and idle connections. It can establish a set of persistent
connections, each of which generated periodic requests to the server. The
effect is that at all times, some of the connections were active while the
rest were idle, and these active and idle connection sets kept changing with
time. (This paragraph was extracted and adapted from the article "Scalability
of Linux Event-Dispatch Mechanisms" (HPL-2000-174), written by Abhishek
Chandra and David Mosberger).
OPTIONS
server IP of the server to connect.
port Port used by server.
numidle Number of idle process to be generated.
EXAMPLE
This is a simple example how to use idleconn:
$ ./idleconn 192.168.1.1 80 100
It would open and maintain 100 idle connections to a web server, listening on
port 80, using the IP address 192.168.1.1.
SEE ALSO
httperf(1)
AUTHOR
The httperf was written by David Mosberger-Tang, Hewlett-Packard Company and Contributors.
This manual page was written by Joao Eriberto Mota Filho <eriberto@debian.org>
for the Debian project (but may be used by others).
0707010000000E000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000001F00000000httperf-0.9.0+git.20201206/man0707010000000F000081ED0000000000000000000000015FCD000200000039000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/man/Makefile.amman_MANS = httperf.1 idleconn.1
EXTRA_DIST = $(man_MANS)
07070100000010000081ED0000000000000000000000015FCD00020000A2EB000000000000000000000000000000000000002900000000httperf-0.9.0+git.20201206/man/httperf.1.\" .IX httperf
.TH "httperf" "1" "01 Feb 2008" "" ""
.SH "NAME"
httperf \- HTTP performance measurement tool
.SH "SYNOPSIS"
.B httperf
.RB [ \-\-add\-header
.I R S ]
.RB [ \-\-burst\-length
.I R N ]
.RB [ \-\-client
.I R I / N ]
.RB [ \-\-close\-with\-reset ]
.RB [ \-d | \-\-debug
.I R N ]
.RB [ \-\-failure\-status
.I R N ]
.RB [ \-h | \-\-help ]
.RB [ \-\-hog ]
.RB [ \-\-http\-version
.I R S ]
.RB [ \-\-max\-connections
.I R N ]
.RB [ \-\-max\-piped\-calls
.I R N ]
.RB [ \-\-method
.I R S ]
.RB [ \-\-no\-host\-hdr ]
.RB [ \-\-num\-calls
.I R N ]
.RB [ \-\-num\-conns
.I R N ]
.RB [ \-\-period " [" d | u | e ] \fIT1\fR [ ,\fIT2\fR ]]
.RB [ \-\-port
.I R N ]
.RB [ \-\-print\-reply " [" header | body ] ]
.RB [ \-\-print\-request " [" header | body ] ]
.RB [ \-\-rate
.I R X ]
.RB [ \-\-recv\-buffer
.I R N ]
.RB [ \-\-retry\-on\-failure ]
.RB [ \-\-send\-buffer
.I R N ]
.RB [ \-\-server
.I R S ]
.RB [ \-\-server\-name
.I R S ]
.RB [ \-\-session\-cookie ]
.RB [ \-\-ssl ]
.RB [ \-\-ssl\-ciphers
.I R L ]
.RB [ \-\-ssl\-no\-reuse ]
.RB [ \-\-think\-timeout
.I R X ]
.RB [ \-\-timeout
.I R X ]
.RB [ \-\-uri
.I R S ]
.RB [ \-v | \-\-verbose ]
.RB [ \-V | \-\-version ]
.RB [ "\-\-wlog y" | n, \fIF\fR]
.RB [ \-\-wsess
.I R N , N , X ]
.RB [ \-\-wsesslog
.I R N , X , F ]
.RB [ \-\-wset
.I R N , X ]
.SH "DESCRIPTION"
.B httperf
is a tool to measure web server performance. It speaks the HTTP
protocol both in its HTTP/1.0 and HTTP/1.1 flavors and offers a
variety of workload generators. While running, it keeps track of a
number of performance metrics that are summarized in the form of
statistics that are printed at the end of a test run. The most basic
operation of
.B httperf
is to generate a fixed number of HTTP GET requests and to measure how
many replies (responses) came back from the server and at what rate
the responses arrived.
.B IMPORTANT:
To obtain correct results, it is necessary to run at most one
.B httperf
process per client machine. Also, there should be as few background
processes as possible both on the client and server machines.
.SH "EXAMPLES"
.TP
httperf \-\-hog \-\-server www
This command causes
.B httperf
to create a connection to host www, send a request for the root
document (http://www/), receive the reply, close the connection, and
then print some performance statistics.
.TP
httperf \-\-hog \-\-server www \-\-num\-conn 100 \-\-ra 10 \-\-timeout 5
Like above, except that a total of 100 connections are created and
that connections are created at a fixed rate of 10 per second. Note
that option ``\-\-rate'' has been abbreviated to ``\-\-ra''.
.TP
httperf \-\-hog \-\-server=www \-\-wsess=10,5,2 \-\-rate 1 \-\-timeout 5
Causes
.B httperf
to generate a total of 10 sessions at a rate of 1 session per second.
Each session consists of 5 calls that are spaced out by 2 seconds.
.TP
httperf \-\-hog \-\-server=www \-\-wsess=10,5,2 \-\-rate=1 \-\-timeout=5 \-\-ssl
Like above, except that
.B httperf
contacts server www via SSL at port 443 (the default port for SSL
connections).
.TP
httperf \-\-hog \-\-server www \-\-wsess=10,5,2 \-\-rate=1 \-\-timeout=5 \-\-ssl \-\-ssl\-ciphers=EXP\-RC4\-MD5:EXP\-RC2\-CBC\-MD5 \-\-ssl\-no\-reuse \-\-http\-version=1.0
Like above, except that
.B httperf
will inform the server that it can only select from two cipher suites
(EXP\-RC4\-MD5 or EXP\-RC2\-CBC\-MD5); furthermore,
.B httperf
will use HTTP version 1.0 which requires a new TCP connection for each
request. Also, SSL session ids are not reused, so the entire SSL
connection establishment process (known as the SSL handshake) occurs
for each connection.
.SH "OPTIONS"
The operation of
.B httperf
can be controlled through a number of options. The tool supports both
short (one\-character) and long (arbitrary\-length) option names. Short
options are prefixed with a single leading dash (\-), long
options with a double\-dash (\-\-). Multiple short options can be
grouped together (e.g.,
.RB `` \-vV ''
is equivalent to
.RB `` "\-v \-V" '')
and long options can be abbreviated so long as they remain unique.
Parameters to options can be specified either by following the long
option name with an equal sign and the parameter value (e.g.,
.BR \-\-burst=10 )
or by separating the option name and value with whitespace (e.g.,
.BR "\-\-burst 10" ).
.TP
.BI \-\-add\-header= S
Specifies to include string
.I S
as an additional request header. It is necessary to specify the
terminating carriage\-return/line\-feed sequence explicitly. This can
be done by using the escape sequence ``\\n''. This makes it possible
to include multiple request headers. For example, ``\-\-add\-header
"Referer: foo\\nAuth: secret\\n"'' would add two request headers
(``Referer'' and ``Auth'') to each request. Other supported escape
sequences are ``\\r'' (carriage\-return), ``\\a'' (line\-feed), ``\\\\''
(backslash), and ``\\N'' where N is the code the character to be
inserted (in octal).
.TP
.BI \-\-burst\-length= N
Specifies the length of bursts. Each burst consists of
.I N
calls to the server. The exact meaning of this parameter depends on
the workload generator. For regular request\-oriented workloads, see the
description of option
.BR \-\-wsess .
.TP
.BR \-\-no\-host\-hdr
Specifies that the "Host:" header should not be included when issuing
an HTTP request.
.TP
.BR \-\-num\-calls .
For session\-oriented workloads, see the description of option
.BR \-\-wsess .
.TP
.BI \-\-client= I / N
Specifies that the machine
.B httperf
is running on is client
.I I
out of a total of
.I N
clients.
.I I
should be in the range from 0 to
.I R N "\-1."
Some of the workload generators (e.g.,
.BR \-\-wset)
use the client identity as a bias value to ensure that not all clients
generate perfectly identical workloads. When performing a test that
involves several client machines, it is generally a good idea to
specify this option.
.TP
.BI \-\-close\-with\-reset
Requests that
.B httperf
closes TCP connections by sending a RESET instead of going through the
normal TCP connection shutdown handshake. Turning on this option can
have ill effects such as data corruption, stuck TCP control blocks, or
wrong results. For this reason, the option should not be used unless
absolutely necessary and even then it should not be used unless its
implications are fully understood.
.TP
.BI \-d= N
.TP
.BI \-\-debug= N
Set debug level to
.I R N .
Larger values of
.I N
will result in more output.
.TP
.BI \-\-failure\-status= N
Specifies that an HTTP response status code of
.I N
should be treated as a failure (i.e., treated as if the request had
timed out, for example). For example, with
.RB `` \-\-failure\-status=504 ''
responses with an HTTP status of ``504 Gateway Time\-out'' would be
considered failures. Caveat: this option is currently supported
for session workloads only (see the
.B \-\-wsess
and
.B \-\-wsesslog
options).
.TP
.B \-h
.TP
.B \-\-help
Prints a summary of available options and their parameters.
.TP
.BI \-\-hog
This option requests to use up as many TCP ports as necessary.
Without this option,
.B httperf
is typically limited to using ephemeral ports (in the range from 1024
to 5000). This limited port range can quickly become a bottleneck so
it is generally a good idea to specify this option for serious
testing. Also, this option must be specified when measuring NT
servers since it avoids a TCP incompatibility between NT and UNIX
machines.
.TP
.BI \-\-http\-version= S
Specifies the version string that should be included in the requests
sent to the server. By default, version string ``1.1'' is used. This
option can be set to ``1.0'' to force the generation of HTTP/1.0
requests. Setting this option to any value other than ``1.0'' or ``1.1''
may result in undefined behavior.
.TP
.BI \-\-max\-connections= N
Specifies that at most
.I N
connections are opened for each session. This option is meaningful in
conjunction with options
.B \-\-wsess
and
.B \-\-wsesslog
only.
.TP
.BI \-\-max\-piped\-calls= N
Specifies that at most
.I N
pipelined calls are issued on each connection. This option is
meaningful in conjunction with options
.B \-\-wsess
and
.B \-\-wsesslog
only.
.TP
.BI \-\-method= S
Specifies the method that should be used when issuing an HTTP request.
If this option is not specified, the GET method is used. The method
.I S
can be an arbitrary string but is usually one of GET, HEAD, PUT, POST,
etc.
.TP
.BI \-\-num\-calls= N
This option is meaningful for request\-oriented workloads only. It
specifies the total number of calls to issue on each connection before
closing it. If
.I N
is greater than 1, the server must support persistent connections.
The default value for this option is 1. If
.BR \-\-burst\-length
is set to
.I R B ,
then the
.I N
calls are issued in bursts of
.I B
pipelined calls each. Thus, the total number of such bursts will
be
.I N/B
(per connection).
.TP
.BI \-\-num\-conns= N
This option is meaningful for request\-oriented workloads only. It
specifies the total number of connections to create. On each
connection, calls are issued as specified by options
.B \-\-num\-calls
and
.BR \-\-burst\-length .
A test stops as soon as the
.I N
connections have either completed or failed. A connection is
considered to have failed if any activity on the connection fails to
make forward progress for more than the time specified by the timeout
options
.B \-\-timeout
and
.BR \-\-think\-timeout .
The default value for this option is 1.
.TP
.BI \-\-period= [D]T1[,T2]
Specifies the time interval between the creation of connections or sessions.
Connections are created by default, sessions if option
.B \-\-wsess
or
.B \-\-wsesslog
has been specified.
This connection/session ``interarrival time'' can alternatively be specified by
the
.B \-\-rate
option, although more flexibility is available with
.B \-\-period.
The
.I D
parameter specifies the interarrival time distribution.
If omitted or set to
.RB `` d '',
a deterministic (i.e., fixed) period is used as specified
by parameter
.I R T1
in units of seconds.
If
.I D
is set to
.RB `` e '',
an exponential (i.e., Poisson) distribution is used with
a mean interarrival time of
.I R T1 .
If
.I D
is set to
.RB `` u '',
a uniform distribution over the interval
.RI [ T1 , T2 )
is used for the interarrival time.
Finally, if
.I D
is set to
.RB ``v'',
a number of rates can be specified as follows:
.B \-\-period=vT1,D1,T2,D2...Tn,Dn
Where n <= NUM_RATES in httperf.h and
.I Ti,Di
represent the period time (i.e., 1/rate) and duration to
maintain that rate (i.e.,
.B \-\-period=v1,2,0.5,4
will generate 1 request/seconds for 2 seconds then
2 requests/seconds for 4 seconds).
In all cases, a period of 0 results in connections
or sessions being generated sequentially (a new connection/session is
initiated as soon as the previous one completes). The default value
for this option is 0. Note that specifying, for example,
.B \-\-rate=5
is equivalent to specifying
.B \-\-period=d0.2
or
.BR \-\-period=0.2 .
By specifying
.BR \-\-period=u1,3 ,
the interarrival times will be randomly chosen from the interval
between 1 and 3 seconds. The specific sequence of (pseudo\-)random
interarrival times are identical from one
.B httperf
run to another as long as the values for the
.B \-\-period
and
.B \-\-client
options are identical.
.TP
.BI \-\-port= N
This option specifies the port number
.I N
on which the web server is listening for HTTP requests. By default,
.B httperf
uses port number 80.
.TP
.BR \-\-print\-reply [ = [ header | body ]]
Requests the printing of the reply headers, body, and summary. The
output is directed to standard output. Reply header lines are
prefixed by "RH", reply body lines are prefixed by "RB", and the
reply\-size summary is prefixed by "RS". The prefix is followed by a
serial number that uniquely identifies the call that the reply line is
for and a colon (":") character that marks the beginning of the actual
reply line. To print only reply headers, pass argument
.B header
to this option. To print only the reply body, pass argument
.B body
to this option.
.TP
.BR \-\-print\-request [ = [ header | body ]]
Requests the printing of the request headers, body (if one is
present), and summary. The output is directed to standard output.
Request header lines are prefixed by "SH", request body lines are
prefixed by "SB", and the request summary is prefixed by "SS". The
prefix is followed by the call's serial number and a colon (":")
character that marks the beginning of the actual reply line. To print
only request headers, pass argument
.B header
to this option. To print only the request body, pass argument
.B body
to this option.
.TP
.BI \-\-rate= X
Specifies the fixed rate at which connections or sessions are created.
Connections are created by default, sessions if option
.B \-\-wsess
or
.B \-\-wsesslog
has been specified. In both cases a rate of 0 results in connections
or sessions being generated sequentially (a new session/connection is
initiated as soon as the previous one completes). The default value
for this option is 0.
.TP
.BI \-\-recv\-buffer= N
Specifies the maximum size of the socket receive buffers used to
receive HTTP replies. By default, the limit is 16KB. A smaller value
may help memory\-constrained clients whereas a larger value may be
necessary when communicating with a server over a high\-bandwidth,
high\-latency connection.
.TP
.BI \-\-retry\-on\-failure
This option is meaningful for session workloads only (see the
.B \-\-wsess
and
.B \-\-wsesslog
options). If specified, a call that results in a failure response (as
defined by the
.B \-\-failure\-status
option) is retried immediately instead of causing the session to fail.
.TP
.BI \-\-send\-buffer= N
Specifies the maximum size of the socket send buffers used to send
HTTP requests. By default, the limit is 4KB. A smaller value may
help memory\-constrained clients whereas a larger value may be
necessary when generating large requests to a server connected via a
high\-bandwidth, high\-latency connection.
.TP
.BI \-\-server= S
Specifies the IP hostname of the server. By default, the hostname
``localhost'' is used. This option should always be specified as it
is generally not a good idea to run the client and the server on the
same machine.
.TP
.BI \-\-server\-name= S
Specifies the (default) server name that appears in the "Host:" header
of every request sent by
.BR httperf .
Without this option, the host name (or IP address) specified by option
.B \-\-server
is used instead.
.TP
.B \-\-session\-cookie
When this option is turned on, cookie managment is enabled on a
per\-session basis. What this means is that if a reply to a request
that was generated by session
.I R X
contains a cookie, then all future requests sent by session
.I X
will include this cookie as well. At present, the cookie manager in
.B httperf
supports only one cookie per session. If a second cookie is received,
the new cookie overwrites the existing one and a warning message is
printed if ``\-\-debug 1'' is on.
.TP
.B \-\-ssl
Specifies that all communication between
.B httperf
and the server should utilize the Secure Sockets Layer (SSL) protocol.
This option is available only if
.B httperf
was compiled with SSL support enabled.
.TP
.BI \-\-ssl\-ciphers= L
This option is only meaningful if SSL is in use (see
.B \-\-ssl
option). This option specifies the list
.I L
of cipher suites that
.B httperf
may use in negotiating a secure connection with the server. If the
list contains more than one cipher suite, the ciphers must be
separated by a colon. If the server does not accept any of the listed
cipher suites, the connection establishment will fail and
.B httperf
will exit immediately. If this option is not specified when the
.B \-\-ssl
option is present then
.B httperf
will use all of the SSLv3 cipher suites provided by the underlying SSL
library.
.TP
.B \-\-ssl\-no\-reuse
This option is only meaningful if SSL and sessions are in use (see
.BR \-\-ssl ,
.BR \-\-wsess ,
.BR \-\-wsesslog ).
When an SSL connection is established the client receives a session
identifier (session id) from the server. On subsequent SSL
connections, the client normally reuses this session id in order to
avoid the expense of repeating the (slow) SSL handshake to establish a
new SSL session and obtain another session id (even if the client
attempts to re\-use a session id, the server may force the client to
renegotiate a session). By default
.B httperf
reuses the session id across all connections in a session. If the
.B \-\-ssl\-no\-reuse
option is in effect, then
.B httperf
will not reuse the session id, and the entire SSL handshake will be
performed for each new connection in a session.
.TP
.BI \-\-think\-timeout= X
Specifies the maximum time that the server may need to initiate
sending the reply for a given request. Note that this timeout value
is added to the normal timeout value (see option
.BR \-\-timeout ).
When accessing static web content, it is usually not necessary to
specify this option. However, when performing tests with long\-running
CGI scripts, it may be necessary to use this option to allow for
larger response\-times. The default value for this option is zero
seconds, meaning that the server has to be able to respond within the
normal timeout value.
.TP
.BI \-\-timeout= X
Specifies the amount of time
.I X
that
.B httperf
is willing to wait for a server reaction. The timeout is specified in
seconds and can be a fractional number (e.g.,
.BR "\-\-timeout 3.5" ).
This timeout value is used when establishing a TCP connection, when
sending a request, when waiting for a reply, and when receiving a
reply. If during any of those activities a request fails to make
forward progress within the alloted time,
.B httperf
considers the request to have died, closes the associated connection
or session and increases the
.B client\-timo
error count. The actual timeout value used when waiting for a reply
is the sum of this timeout and the think\-timeout (see option
.BR \-\-think\-timeout ).
By default, the timeout value is infinity.
.TP
.BI \-\-uri= S
Specifies that URI
.I S
should be accessed on the server. For some of the workload generators
(e.g.,
.BR \-\-wset ),
this option specifies the prefix for the URIs being accessed.
.TP
.BI \-\-use\-timer\-cache
This feature allows the user to specify whether they want to
cache timestamps or not. Timestamps are not cached by default, but
the user can enable caching if higher performance is more important
than timing accuracy. For small response sizes, disabling timer
caching reduced the performance of httperf by about
10%; for larger response sizes there was little or no effect.
.TP
.B \-v
.TP
.B \-\-verbose
Puts
.B httperf
into verbose mode. In this mode, additional output such as the
individual reply rate samples and connection lifetime histogram are
printed.
.TP
.B \-V
.TP
.B \-\-version
Prints the version of
.BR httperf .
.TP
.BI \-\-wlog= B , F
This option can be used to generate a specific sequence of URI
accesses. This is useful to replay the accesses recorded in a server
log file, for example. Parameter
.I F
is the name of a file containing the ASCII NUL separated list of URIs
that should be accessed. If parameter
.I B
is set to
.RB `` y '',
.B httperf
will wrap around to the beginning of the file when reaching the end of
the list (so the list of URIs is accessed repeatedly). With
.I B
set to
.RB `` n '',
the test will stop no later than when reaching the end of the URI
list.
.TP
.BI \-\-wsess= N1 , N2 , X
Requests the generation and measurement of sessions instead of
individual requests. A session consists of a sequence of bursts which
are spaced out by the user think\-time. Each burst consists of a fixed
number
.I L
of calls to the server
.RI ( L
is specified by option
.BR \-\-burst\-length ).
The calls in a burst are issued as follows: at first, a single call is
issued. Once the reply to this first call has been fully received,
all remaining calls in the burst are issued concurrently. The
concurrent calls are issued either as pipelined calls on an existing
persistent connection or as individual calls on separate connections.
Whether a persistent connection is used depends on whether the server
responds to the first call with a reply that includes a ``Connection:
close'' header line. If such a line is present, separate connections
are used.
The option specifies the following parameters:
.I N1
is the total number of sessions to generate,
.I N2
is the number of calls per session, and
.I X
is the user think\-time (in seconds) that separates consecutive call
bursts. For example, the options
.RB `` "\-\-wsess=100,50,10 \-\-burst\-len 5" ''
would result in 100 sessions with a total of 50 calls each. Since
each burst has a length of 5 calls, a total of 10 call bursts would be
generated per session. The user think\-time between call bursts would
be 10 seconds. Note that user think\-time
.I X
denotes the time between receiving the last reply of the previous
call burst and the sending of the first request of the next burst.
A test involving sessions finishes as soon as the requested number
.I N1
of sessions have either failed or completed. A session is considered
to have failed if any operation in a session takes longer than
the timeouts specified by options
.B \-\-timeout
and
.BR \-\-think\-timeout .
In addition, a session also fails if the server returns a reply with a
status code matching the one specified by option
.BR \-\-failure\-status .
.TP
.BI \-\-wsesslog= N , X , F
This specifies a session workload generator similar to
.B \-\-wsess
(please read that description first). With
.B \-\-wsesslog
though, many aspects of user sessions, including the number and
sequence of URI's, request method, think\-time and burst\-length parameters,
can be specified in an input file
.I F.
Two other parameters are retained from
.B \-\-wsess,
namely
.I N,
the number of sessions to initiate, and
.I X,
the burst\-to\-burst user think time (note that this becomes a default
time since the input file
.I F
can also specify user think time on a per\-burst basis.
A small example input file can most\-easily show the settable parameters:
.br
.br
# Comment lines start with a ``#'' as the first
.br
# character. Lines with only whitespace delimit
.br
# sessions (multiple blank lines do not generate
.br
# ``null'' sessions). All other lines specify a
.br
# uri\-sequence (1 uri per line). If the first
.br
# character of the line is whitespace (e.g. space
.br
# or tab), the uri is considered to be part of a
.br
# burst that is sent out after the previous
.br
# non\-burst uri.
.br
.br
# session 1 definition (this is a comment)
.br
/foo.html think=2.0
.br
/pict1.gif
.br
/pict2.gif
.br
/foo2.html method=POST contents='Post data'
.br
/pict3.gif
.br
/pict4.gif
.br
.br
# session 2 definition
.br
/foo3.html method=POST contents="Multiline\\ndata"
.br
/foo4.html method=HEAD
.br
.br
The above description specifies 2 sessions. The first session will
start with a request for /foo.html. When the /foo.html response comes
back, a burst of 2 requests will follow (/pict1.gif and /pict2.gif).
When the last of those responses is received, a two second user think
time is inserted before the next request of /foo2.html is issued.
This request is sent as a POST. The posted data can be contained
between single\- or double\-quotes. Newlines can appear within posted
data as ``\\n'' or as a ``\\<CR>''. The /foo2.html response is
followed by a burst request of /pict3.gif and /pict4.gif, which
concludes this session. The second session is started some time after
the first, as specified by the
.B \-\-rate
or
.B \-\-period
options.
.br
.br
The second session consists of 2 requests separated by the default user think
time as specified by the
.I X
parameter of the
.B \-\-wsesslog
option. If the
.I N
parameter of
.B \-\-wsesslog
is greater than the number of sessions defined in input file
.I R F ,
then the defined sessions are used repeatedly until
.I N
sessions have been created (i.e., the defined sessions are used in a
round\-robin fashion).
.br
.br
One should avoid using
.B \-\-wsesslog
in conjunction with other
.B httperf
options that also control session behavior and workload URI's, namely
.B \-\-burst\-length,
.B \-\-wsess,
.B \-\-wlog,
and
.B \-\-wset.
.TP
.BI \-\-wset= N , X
This option can be used to walk through a list of URIs at a given
rate. Parameter
.I N
specifies the number of distinct URIs that should be generated and
.I X
specifies the rate at which new URIs are accessed. A rate of
.B 0.25
would mean that the same URI would be accessed four times in a row
before moving on to the next URI. This type of access pattern is
useful in generating a workload that induces a relatively predictable
amount of traffic in the disk I/O subsystem of the server (assuming
.I N
and the accessed files are big enough to exceed the server's buffer
cache). The URIs generated are of the form
.I R prefix / path .html,
where
.I prefix
is the URI prefix specified by option
.B \-\-uri
and
.I path
is generated as follows: for the
.I R i \-th
file in the working set, write down
.I i
in decimal, prefixing the number with as many zeroes as necessary
to get a string that has as many digits as
.I R N \-1.
Then insert a slash character between each digit. For example,
the 103rd file in a working set consisting of 1024 files would
result in a path of
.RB `` 0/1/0/3 ''.
Thus, if the URI\-prefix is
.BR /wset1024 ,
then the URI being accessed would be
.BR /wset1024/0/1/0/3.html .
In other words, the files on the server need to be organized as a
10ary tree.
.SH "OUTPUT"
This section describes the statistics output at the end of each test
run. The basic information shown below is printed independent of the
selected workload generator.
.PP
.RS
.br
.B Total:
connections 30000 requests 29997 replies 29997 test\-duration 299.992 s
.PP
.B Connection rate:
100.0 conn/s (10.0 ms/conn, <=14 concurrent connections)
.br
.B Connection time [ms]:
min 1.4 avg 3.0 max 163.4 median 1.5 stddev 7.3
.br
.B Connection time [ms]:
connect 0.6
.br
.B Connection length [replies/conn]:
1.000
.PP
.B Request rate:
100.0 req/s (10.0 ms/req)
.br
.B Request size [B]:
75.0
.PP
.B Reply rate [replies/s]:
min 98.8 avg 100.0 max 101.2 stddev 0.3 (60 samples)
.br
.B Reply time [ms]:
response 2.4 transfer 0.0
.br
.B Reply size [B]:
header 242.0 content 1010.0 footer 0.0 (total 1252.0)
.br
.B Reply status:
1xx=0 2xx=29997 3xx=0 4xx=0 5xx=0
.PP
.B CPU time [s]:
user 94.31 system 205.26 (user 31.4% system 68.4% total 99.9%)
.br
.B Net I/O:
129.6 KB/s (1.1*10^6 bps)
.PP
.B Errors:
total 3 client\-timo 0 socket\-timo 0 connrefused 3 connreset 0
.br
.B Errors:
fd\-unavail 0 addrunavail 0 ftab\-full 0 other 0
.br
.RE
.PP
There are six groups of statistics: overall results (``Total''),
connection related results (``Connection''), results relating to the
issuing of HTTP requests (``Request''), results relating to the replies
received from the server (``Reply''), miscellaneous results relating to
the CPU (``CPU'') and network (``Net I/O'') utilization and, last but not
least, a summary of errors encountered (``Errors'').
.TP
Total Section
.br
This section summarizes how many TCP connections were initiated by
.BR httperf ,
how many requests it sent out, how many replies it received, and
what the total test duration was. In the example output
shown above, 30,000 connections were created, 29,997 requests were
sent out and 29,997 replies were received. The duration of the
test was almost exactly 5 minutes (300 seconds).
.TP
Connection Section
.br
This section conveys information related to TCP connections generated
by the tool. Specifically, the ``Connection rate'' line shows that new
connections were initiated at a rate of 100.0 connections per second.
This rate corresponds to a period of 10.0 milliseconds per
connection. The last number in this line shows that at most 14
connections were open at any given time.
The first line labeled ``Connection time'' gives lifetime statistics
for successful connections. The lifetime of a connection is the time
between a TCP connection is initiated and the time the connection is
closed. A connection is considered successful if it had at least one
call that completed successfully. In the example output, the line
indicates that the minimum (``min'') connection lifetime was 1.4
milliseconds, the average (``avg'') lifetime was 3.0 milliseconds, the
maximum (``max'') was 163.4 milliseconds, the median (``median'')
lifetime was 1.5 milliseconds, and that the standard deviation of the
lifetimes was 7.3 milliseconds. The median lifetime is computed based
on a histogram with one millisecond resolution and a maximum lifetime
of 100 seconds. Thus, the median is accurate to within half a
millisecond if at least half of the successful connections have a
lifetime of no more than 100 seconds.
The next statistic in this section is the average time it took to
establish a TCP connection. Only successful TCP connection
establishments are counted. In the example, the second line labeled
``Connection time'' shows that, on average, it took 0.6 milliseconds
to establish a connection.
The final line in this section is labeled ``Connection length.'' It
gives the average number of replies received on each connection that
received at least one reply (i.e., connections that failed before
yielding the first reply are not counted). This number can be bigger
than 1.0 due to persistent connections.
.TP
Request Section
.br
The line labeled ``Request rate'' gives the rate at which HTTP requests
were issued and the period that this rate corresponds to. In the
example above, the request rate was 100.0 requests per second, which
corresponds to 10.0 milliseconds per request. As long as no
persistent connections are employed, the results in this section are
very similar or identical to results in the connection section.
However, when persistent connections are used, several calls can be
performed on a single connection in which case the results would be
different.
The line labeled ``Request size'' gives the average size of the HTTP
requests in bytes. In the example above, the average request size was
75 bytes.
.TP
Reply Section
.br
For simple measurements, this section is often the most interesting
one as the line labeled ``Reply rate'' gives various statistics for
the reply rate. In the example above, the minimum (``min'') reply
rate was 98.8 replies per second, the average (``avg'') was 100
replies per second, and the maximum (``max'') rate was 101.2 replies
per second. The standard deviation was 0.3 replies per second. The
number enclosed in parentheses shows that 60 reply rate samples were
acquired. At present,
.B httperf
collects a rate sample once every five seconds. To obtain a
meaningful standard deviation, it is recommended to run tests long
enough so at least thirty samples are obtained. This corresponds to a
test duration of at least 150 seconds.
The line labeled ``Reply Time'' gives information on how long it took
for the server to respond and how long it took to receive the reply.
In the example, it took on average 2.4 milliseconds between sending
the first byte of the request and receiving the first byte of the
reply. The time to ``transfer'', or read, the reply was too short to
be measured, so it shows up as zero. The is typical when the entire
reply fits into a single TCP segment.
The next line, labeled ``Reply size'' contains statistics on the
average size of the replies\-\-\-all numbers are in reported bytes.
Specifically, the line lists the average length of reply headers, the
content, and footers (HTTP/1.1 uses footers to realize the ``chunked''
transfer encoding). For convenience, the average total number of
bytes in the replies is also given in parentheses. In the example,
the average header length (``header'') was 242 bytes, the average
content length (``content'') was 1010 bytes, and there were no footers
(``footer'' length is zero). The total reply length of 1252 bytes on
average.
The final line in this section is a histogram of the major status
codes received in the replies from the server. The major status code
is the ``hundreds''\-digit of the full HTTP status code. In the
example, all 29,997 replies had a major status code of 2. It's a good
guess that all status codes were ``200 OK'' but the information in the
histogram is not detailed enough to allow distinguishing status codes
with the same major code.
.TP
Miscellaneous Section
.br
This section starts with a summary of the CPU utilization on the
client machine. In the example, the line labeled ``CPU time'' shows
that 94.31 seconds were spent executing in user mode (``user''),
205.26 seconds were spent executing in system mode (``system'') and
that this corresponds to 31.4% user mode execution and 68.4% system
execution. The total utilization was 99.9%, which is expected given
that
.B httperf
is a CPU hog. A total CPU utilization of significantly less than 100%
is a sign that there were competing processes that interfered with the
test.
The line labeled ``Net I/O'' gives the average network throughput in
kilobytes per second (where a kilobyte is 1024 bytes) and in megabits
per second (where a megabit is 10^6 bits). In the example, an average
network usage of about 129.6 kilobytes per second was sustained. The
number in parentheses shows that this corresponds to about 1.1
megabits per second. This network bandwidth is computed based on the
number of bytes sent and received on the TCP connections. In other
words, it does not account for the network headers or TCP
retransmissions that may have occurred.
.TP
Errors Section
.br
The last section contains statistics on the errors that were
encountered during a test. In the example, the two lines labeled
``Errors'' show that there were a total of three errors and that all
three errors were due to the server refusing to accept a connection
(``connrefused''). A description of each error counter follows:
.B client\-timo:
The number of times a session, connection, or call failed due
to a client timeout (as specified by the
.B \-\-timeout
and
.BR \-\-think\-timeout )
options.
.B socket\-timo:
The number of times a TCP connection failed with a socket\-level
timeout (ETIMEDOUT).
.B connrefused:
The number of times a TCP connection attempt failed with a
``connection refused by server'' error (ECONNREFUSED).
.B connreset:
The number of times a TCP connection failed due to a RESET from the
server. Typically, a RESET is received when the client attempts to
send data to the server at a time the server has already closed its
end of the connection. NT servers also send RESETs when attempting to
establish a new connection when the listen queue is full.
.B fd\-unavail:
The number of times the
.B httperf
process was out of file descriptors. Whenever this count is non\-zero,
the test results are meaningless because the client was overloaded
(see section "CHOOSING TIMEOUT VALUES").
.B addrunavail:
The number of times the client was out of TCP port numbers
(EADDRNOTAVAIL). This error should never occur. If it does, the
results should be discarded.
.B ftab\-full:
The number of times the system's file descriptor table is full.
Again, this error should never occur. If it does, the results should
be discarded.
.B other:
The number of times some other type of error occurred. Whenever this
counter is non\-zero, it is necessary to track down the real cause of
the error. To assist in doing this,
.B httperf
prints the error code (errno) of the first unknown errors that occurs
during a test run.
.RE
.PP
When
.B \-\-wsess
or
.B \-\-wsesslog
is specified,
.B httperf
generates and measures sessions instead of individual calls and
additional statistics are printed at the end of a test. An example
output is shown below.
.PP
.RS
.B Session rate [sess/s]:
min 0.00 avg 0.59 max 2.40 stddev 0.37 (240/450)
.br
.B Session:
avg 6.45 connections/session
.br
.B Session lifetime [s]:
123.9
.br
.B Session failtime [s]:
58.5
.br
.B Session length histogram:
4 7 4 ... 3 3 240
.RE
.PP
The line labeled ``Session rate'' shows the minimum, average, and
maximum rate at which sessions completed (based on a 5 second sampling
interval). It also shows the standard deviation of the session
completion rate. The numbers in parentheses show how many sessions
succeeded and how many sessions were initiated. In the example above,
the minimum, average, and maximum session completion rates were 0.00,
0.59, and 2.40 sessions per second, respectively. The standard
deviation was 0.37 sessions per second and 240 out of 450 sessions
completed successfully (210 failed due to errors such as timeouts).
The next line, labeled ``Session:'' shows the average length of a
session measured in connections. In the example above, an average of
6.45 connections were required to complete a session.
The line labeled ``Session lifetime'' gives the average time it took
to complete a successful session. In the example above, it took an
average of 123.9 seconds.
The line labeled ``Session failtime'' gives the average time it took
before an unsuccessful session failed. In the example above, it took
on average 58.5 seconds for a session to fail.
Finally, the line labeled ``Session length histogram'' gives a
histogram of the number of replies received by each session. In the
example above, 4 sessions ended after receiving no reply at all, 7
ended after receiving one reply, and so on (the ellipsis indicates
additional histogram counts that were omitted from this manual for
space reasons). Note that this histogram does not distinguish between
successful and failed sessions.
.SH "CHOOSING TIMEOUT VALUES"
Since the machine that
.B httperf
runs on has only a finite set of resource available, it can not
sustain arbitrarily high HTTP loads. For example, one limiting factor
is that there are only roughly 60,000 TCP port numbers that can be in
use at any given time. Since on most UNIX systems it takes one minute
for a TCP connection to be fully closed (leave the TIME_WAIT state),
the maximum rate a client can sustain is at most 1,000 requests per
second.
The actual sustainable rate is often much lower than that because
before running out of TCP ports, the machine is likely to run out of
file descriptors (one file descriptor is used up for each open TCP
connection). By default, HP\-UX 10.20 allows 1,024 open file
descriptors per process. This means that without extra precautions,
.B httperf
could potentially very quickly use up all available file descriptors,
at which point it could not induce any additional load on the server.
To avoid this problem,
.B httperf
provides option
.B \-\-timeout
to set a timeout for all communication with the server. If the server
does not respond before the timeout expires, the client considers the
corresponding session, connection, or call to be ``dead,'' closes the
associated TCP connection, and increases the ``client\-timo'' error
count. The only exception to this rule is that after sending an
entire request to the server,
.B httperf
allows the server to take some additional time before it starts
sending the reply. This is to accommodate HTTP requests that take a
long time to complete on the server. This additional time is called
the ``server think time'' and can be specified by option
.BR \-\-think\-timeout .
By default, this additional think time is zero seconds, so the server
would always have to respond within the time alloted by option
.BR \-\-timeout .
Timeouts allow
.B httperf
to sustain high offered loads even when the server is overloaded. For
example, with a timeout of 2 seconds and assuming that 1,000
file\-descriptors are available, the offered load could be up to 500
requests per second (in practice, the sustainable load is often
somewhat smaller than the theoretical value). On the downside,
timeouts artificially truncate the connection lifetime distribution.
Thus, it is recommended to pick a timeout value that is as large as
possible yet small enough to allow sustaining the desired offered
rate. A timeout as short as one second may be acceptable, but larger
timeouts (5\-10 seconds) are preferable.
It is important to keep in mind that timeouts do not guarantee that a
client can sustain a particular offered load\-\-\-there are many other
potential resource bottlenecks. For example, in some cases the client
machine may simply run out of CPU time. To ensure that a given test
really measured the server's capabilities and not the client's, it is
a good idea to vary the number of machines participating in a test.
If observed performance remains the same as the number of client
machines is varied, the test results are likely to be valid.
.SH "AUTHOR"
.BR httperf
was developed by David Mosberger and was heavily influenced by an
earlier tool written by Tai Jin. Stephane Eranian contributed the
log\-file based URI generator. Dick Carter contributed the
.B \-\-wsesslog
workload generator, the support behind the
.B \-\-period
option, and bug fixes. Ted Bullock has taken over maintenance and
development activities since September 2006.
.SH "BUGS"
Probably many. Always be sure to double\-check results and don't fall
prey to measuring client\-performance instead of server performance!
.PP
The user\-interface definitely could be improved. A simple workload
description language might be more suitable than the dozens of little
command\-line options the tool has right now.
07070100000011000081A40000000000000000000000015FCD000200000604000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/man/idleconn.1.TH idleconn "1" "Mar 2016" "IDLECONN 0.9.0" "tool for opening any number of idle connections"
.\" Text automatically generated by txt2man
.SH NAME
\fBidleconn \fP- tool for opening any number of idle connections
\fB
.SH SYNOPSIS
.nf
.fam C
\fBidleconn\fP <server> <port> <numidle>
.fam T
.fi
.fam T
.fi
.SH DESCRIPTION
\fBidleconn\fP is part of httperf suite and is useful to simulate a large number
of concurrent and idle connections. It can establish a set of persistent
connections, each of which generated periodic requests to the server. The
effect is that at all times, some of the connections were active while the
rest were idle, and these active and idle connection sets kept changing with
time. (This paragraph was extracted and adapted from the article "Scalability
of Linux Event-Dispatch Mechanisms" (HPL-2000-174), written by Abhishek
Chandra and David Mosberger).
.SH OPTIONS
.TP
.B
server
IP of the server to connect.
.TP
.B
port
Port used by server.
.TP
.B
numidle
Number of idle process to be generated.
.SH EXAMPLE
This is a simple example how to use \fBidleconn\fP:
.PP
.nf
.fam C
$ ./idleconn 192.168.1.1 80 100
.fam T
.fi
It would open and maintain 100 idle connections to a web server, listening on
port 80, using the IP address 192.168.1.1.
.SH SEE ALSO
\fBhttperf\fP(1)
.SH AUTHOR
The httperf was written by David Mosberger-Tang, Hewlett-Packard Company and Contributors.
.PP
This manual page was written by Joao Eriberto Mota Filho <eriberto@debian.org>
for the Debian project (but may be used by others).
07070100000012000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000001F00000000httperf-0.9.0+git.20201206/src07070100000013000081ED0000000000000000000000015FCD000200000207000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/Makefile.amSUBDIRS = gen lib stat
# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(srcdir) -I$(srcdir)/gen -I$(srcdir)/lib -I$(srcdir)/stat
bin_PROGRAMS = httperf
if IDLECONN
bin_PROGRAMS += idleconn
idleconn_SOURCES = idleconn.c
idleconn_LDADD = lib/libutil.a
endif
httperf_SOURCES = httperf.c httperf.h object.c object.h call.c call.h conn.c \
conn.h sess.c sess.h core.c core.h localevent.c localevent.h http.c http.h \
timer.c timer.h
httperf_LDADD = gen/libgen.a lib/libutil.a stat/libstat.a
07070100000014000081ED0000000000000000000000015FCD000200000A93000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/call.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#include "config.h"
#include <stdio.h>
#include <generic_types.h>
#include <object.h>
#include <call.h>
#include <httperf.h>
static u_long next_id = 0;
void
call_init (Call *c)
{
# define DEFAULT_METHOD "GET"
c->id = next_id++;
call_set_method (c, DEFAULT_METHOD, sizeof (DEFAULT_METHOD) - 1);
c->req.version = param.http_version;
c->req.iov[IE_BLANK].iov_base = (caddr_t) " ";
c->req.iov[IE_BLANK].iov_len = 1;
c->req.iov[IE_NEWLINE1].iov_base = (caddr_t) "\r\n";
c->req.iov[IE_NEWLINE1].iov_len = 2;
c->req.iov[IE_NEWLINE2].iov_base = (caddr_t) "\r\n";
c->req.iov[IE_NEWLINE2].iov_len = 2;
}
void
call_deinit (Call *call)
{
}
int
call_append_request_header (Call *c, const char *hdr, size_t len)
{
u_int num_hdrs = c->req.num_extra_hdrs;
if (num_hdrs >= MAX_EXTRA_HEADERS)
{
fprintf (stderr, "%s.call_append_request_header: max headers "
"(%d) exceeded.\n", prog_name, MAX_EXTRA_HEADERS);
return -1;
}
c->req.iov[IE_FIRST_HEADER + num_hdrs].iov_base = (caddr_t) hdr;
c->req.iov[IE_FIRST_HEADER + num_hdrs].iov_len = len;
c->req.num_extra_hdrs = num_hdrs + 1;
return 0;
}
07070100000015000081ED0000000000000000000000015FCD000200001339000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/call.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef call_h
#define call_h
#include <sys/uio.h>
/* Max. # of additional request header lines we allow: */
#define MAX_EXTRA_HEADERS 4
typedef enum IOV_Element
{
IE_METHOD,
IE_BLANK, /* space separating method from location */
IE_LOC, /* for proxy requests only */
IE_URI,
IE_PROTL,
IE_HOST, /* for the "Host:" header */
IE_NEWLINE1,
IE_FIRST_HEADER,
IE_LAST_HEADER = IE_FIRST_HEADER + MAX_EXTRA_HEADERS - 1,
IE_NEWLINE2,
IE_CONTENT,
IE_LEN /* must be last */
}
IOV_Element;
/* I call this a "call" because "transaction" is too long and because
it's basically a remote procedure call consisting of a request that
is answered by a reply. */
typedef struct Call
{
Object obj;
u_long id; /* unique id */
/* Connection this call is being sent on. This pointer is NOT
reference counted as otherwise we would get a recursive
dependency between connections and calls.... */
struct Conn *conn;
struct Call *sendq_next;
struct Call *recvq_next;
Time timeout; /* used for watchdog management */
struct
{
Time time_send_start;
Time time_recv_start;
}
basic;
/* the request: */
struct
{
int version; /* 0x10000*major + minor */
u_int num_extra_hdrs; /* number of additional headers in use */
int iov_index; /* first iov element that has data */
size_t size; /* # of bytes sent */
struct iovec iov_saved; /* saved copy of iov[iov_index] */
struct iovec iov[IE_LEN];
}
req;
/* the reply: */
struct
{
int status;
int version; /* 0x10000*major + minor */
size_t header_bytes; /* # of header bytes received so far */
size_t content_bytes; /* # of reply data bytes received so far */
size_t footer_bytes; /* # of footer bytes received so far */
}
reply;
}
Call;
/* Initialize the new call object C. */
extern void call_init (Call *c);
/* Destroy the call-specific state in call object C. */
extern void call_deinit (Call *c);
#define call_new() ((Call *) object_new (OBJ_CALL))
#define call_inc_ref(c) object_inc_ref ((Object *) (c))
#define call_dec_ref(c) object_dec_ref ((Object *) (c))
/* Append the additional request header line(s) HDR to the request
headers. The total length of the additional headers is LEN bytes.
The headers must be end with a carriage-return, line-feed sequence
("\r\n"). */
extern int call_append_request_header (Call *c, const char *hdr, size_t len);
#define call_set_method(c, method, method_len) \
do \
{ \
c->req.iov[IE_METHOD].iov_base = (caddr_t) method; \
c->req.iov[IE_METHOD].iov_len = method_len; \
} \
while (0)
#define call_set_location(c, loc, loc_len) \
do \
{ \
c->req.iov[IE_LOC].iov_base = (caddr_t) loc; \
c->req.iov[IE_LOC].iov_len = loc_len; \
} \
while (0)
#define call_set_uri(c, uri, uri_len) \
do \
{ \
c->req.iov[IE_URI].iov_base = (caddr_t) uri; \
c->req.iov[IE_URI].iov_len = uri_len; \
} \
while (0)
#define call_set_contents(c, content, content_len) \
do \
{ \
c->req.iov[IE_CONTENT].iov_base = (caddr_t) content; \
c->req.iov[IE_CONTENT].iov_len = content_len; \
} \
while (0)
#endif /* call_h */
07070100000016000081ED0000000000000000000000015FCD000200001076000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/conn.c/*
* httperf -- a tool for measuring web server performance
* Copyright 2000-2007 Hewlett-Packard Company
*
* This file is part of httperf, a web server performance measurment
* tool.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of this work with the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the same
* license as the "OpenSSL" library), and distribute linked combinations
* including the two. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
static char *srvbase, *srvend, *srvcurrent;
void
conn_add_servers(void)
{
struct stat st;
int fd, len;
fd = open(param.servers, O_RDONLY, 0);
if (fd == -1)
panic("%s: can't open %s\n", prog_name, param.servers);
fstat(fd, &st);
if (st.st_size == 0)
panic("%s: file %s is empty\n", prog_name, param.servers);
srvbase = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (srvbase == (char *)-1)
panic("%s: can't mmap the file: %s\n", prog_name, strerror(errno));
close(fd);
srvend = srvbase + st.st_size;
for (srvcurrent = srvbase; srvcurrent < srvend; srvcurrent += len + 1) {
len = strlen(srvcurrent);
core_addr_intern(srvcurrent, len, param.port);
}
srvcurrent = srvbase;
}
void
conn_init(Conn *conn)
{
if (param.servers) {
int len = strlen(srvcurrent);
conn->hostname = srvcurrent;
conn->hostname_len = len;
conn->fqdname = conn->hostname;
conn->fqdname_len = conn->hostname_len;
srvcurrent += len + 1;
if (srvcurrent >= srvend)
srvcurrent = srvbase;
} else if (param.server_name) {
conn->hostname = param.server;
conn->hostname_len = strlen(param.server);
conn->fqdname = param.server_name;
conn->fqdname_len = strlen(param.server_name);
} else {
conn->hostname = param.server;
conn->hostname_len = strlen(param.server);
conn->fqdname = conn->hostname;
conn->fqdname_len = conn->hostname_len;
}
conn->port = param.port;
conn->sd = -1;
conn->myport = -1;
conn->line.iov_base = conn->line_buf;
conn->fqdname_len = strcspn(conn->fqdname,"\r\n"); // Chomp since used in Host header record
#ifdef HAVE_SSL
if (param.use_ssl) {
conn->ssl = SSL_new(ssl_ctx);
if (!conn->ssl) {
ERR_print_errors_fp(stderr);
exit(-1);
}
if (param.tls_server_name)
{
SSL_set_tlsext_host_name(conn->ssl, param.tls_server_name);
}
if (param.ssl_cipher_list) {
/* set order of ciphers */
int ssl_err = SSL_set_cipher_list(conn->ssl, param.ssl_cipher_list);
if (DBG > 2)
fprintf(stderr,
"core_ssl_connect: set_cipher_list returned %d\n",
ssl_err);
}
}
#endif
}
void
conn_deinit(Conn *conn)
{
assert(conn->sd < 0 && conn->state != S_FREE);
assert(!conn->sendq);
assert(!conn->recvq);
assert(!conn->watchdog);
conn->state = S_FREE;
#ifdef HAVE_SSL
if (param.use_ssl)
SSL_free(conn->ssl);
#endif
}
07070100000017000081ED0000000000000000000000015FCD00020000108C000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/conn.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef conn_h
#define conn_h
#include "config.h"
#include <sys/uio.h>
#ifdef HAVE_SSL
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
/* Maximum header line length that we can process properly. Longer
lines will be treated as if they were only this long (i.e., they
will be truncated). */
#define MAX_HDR_LINE_LEN 1024
struct Call;
typedef enum Conn_State
{
S_INITIAL,
S_CONNECTING,
S_CONNECTED,
S_REPLY_STATUS,
S_REPLY_HEADER,
S_REPLY_CONTINUE,
S_REPLY_DATA,
S_REPLY_CHUNKED,
S_REPLY_FOOTER,
S_REPLY_DONE,
S_CLOSING,
S_FREE
}
Conn_State;
struct local_addr;
typedef struct Conn
{
Object obj;
Conn_State state;
struct Conn *next;
struct Call *sendq; /* calls whose request needs to be sent */
struct Call *sendq_tail;
struct Call *recvq; /* calls waiting for a reply */
struct Call *recvq_tail;
struct Timer *watchdog;
struct
{
Time time_connect_start; /* time connect() got called */
u_int num_calls_completed; /* # of calls that completed */
}
basic; /* maintained by stat/stats_basic.c */
size_t hostname_len;
const char *hostname; /* server's hostname (or 0 for default) */
size_t fqdname_len;
const char *fqdname; /* fully qualified server name (or 0) */
int port; /* server's port (or -1 for default) */
int sd; /* socket descriptor */
int myport; /* local port number or -1 */
struct local_addr *myaddr;
/* Since replies are read off the socket sequentially, much of the
reply-processing related state can be kept here instead of in
the reply structure: */
struct iovec line; /* buffer used to parse reply headers */
size_t content_length; /* content length (or INF if unknown) */
u_int has_body : 1; /* does reply have a body? */
u_int is_chunked : 1; /* is the reply chunked? */
u_int reading : 1;
u_int writing : 1;
char line_buf[MAX_HDR_LINE_LEN]; /* default line buffer */
#ifdef HAVE_SSL
SSL *ssl; /* SSL connection info */
#endif
#ifdef HAVE_EPOLL
int epoll_added; /* is fd added into epoll? */
#endif
}
Conn;
extern int max_num_conn;
extern Conn *conn;
/* Store the servers to connect to in memory. */
extern void conn_add_servers (void);
/* Initialize the new connection object C. */
extern void conn_init (Conn *c);
/* Destroy the connection-specific state in connection object C. */
extern void conn_deinit (Conn *c);
#define conn_new() ((Conn *) object_new (OBJ_CONN))
#define conn_inc_ref(c) object_inc_ref ((Object *) (c))
#define conn_dec_ref(c) object_dec_ref ((Object *) (c))
#endif /* conn_h */
07070100000018000081ED0000000000000000000000015FCD000200009968000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/core.c/*
* Copyright (C) 2000-2007 Hewlett-Packard Company
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#ifdef __FreeBSD__
#define HAVE_KEVENT
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#endif
#ifdef HAVE_KEVENT
#include <sys/event.h>
/*
* Older systems using kevent() always specify the time in
* milliseconds and do not have a flag to select a different scale.
*/
#ifndef NOTE_MSECONDS
#define NOTE_MSECONDS 0
#endif
#endif
#ifdef __FreeBSD__
#include <ifaddrs.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <generic_types.h>
#include <sys/resource.h> /* after sys/types.h for BSD (in generic_types.h) */
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <http.h>
#define HASH_TABLE_SIZE 1024 /* can't have more than this many servers */
#define MIN_IP_PORT IPPORT_RESERVED
#define MAX_IP_PORT 65535
#define BITSPERLONG (8*sizeof (u_long))
struct local_addr {
struct in_addr ip;
u_long port_free_map[((MAX_IP_PORT - MIN_IP_PORT + BITSPERLONG)
/ BITSPERLONG)];
u_long mask;
int previous;
};
struct address_pool {
struct local_addr *addresses;
int count;
int last;
};
static volatile int running = 1;
static int iteration;
static u_long max_burst_len;
#ifdef HAVE_KEVENT
static int kq, max_sd = 0;
#else
#ifdef HAVE_EPOLL
#define EPOLL_N_MAX 8192
static int epoll_fd, max_sd = 0;
static struct epoll_event *epoll_events;
static int epoll_timeout;
#else
static fd_set rdfds, wrfds;
static int min_sd = 0x7fffffff, max_sd = 0, alloced_sd_to_conn = 0;
static struct timeval select_timeout;
#endif
#endif
static struct sockaddr_in myaddr;
static struct address_pool myaddrs;
#ifndef HAVE_KEVENT
Conn **sd_to_conn;
#endif
static char http10req[] =
" HTTP/1.0\r\nUser-Agent: httperf/" VERSION
"\r\nConnection: keep-alive\r\nHost: ";
static char http11req[] =
" HTTP/1.1\r\nUser-Agent: httperf/" VERSION "\r\nHost: ";
static char http10req_nohost[] =
" HTTP/1.0\r\nUser-Agent: httperf/" VERSION
"\r\nConnection: keep-alive";
static char http11req_nohost[] =
" HTTP/1.1\r\nUser-Agent: httperf/" VERSION;
#ifndef SOL_TCP
# define SOL_TCP 6 /* probably ought to do getprotlbyname () */
#endif
#ifdef TIME_SYSCALLS
# define SYSCALL(n,s) \
{ \
Time start, stop; \
do \
{ \
errno = 0; \
start = timer_now_forced (); \
s; /* execute the syscall */ \
stop = timer_now_forced (); \
syscall_time[SC_##n] += stop - start; \
++syscall_count[SC_##n]; \
} \
while (errno == EINTR); \
}
enum Syscalls {
SC_BIND, SC_CONNECT, SC_READ, SC_SELECT, SC_SOCKET, SC_WRITEV,
SC_SSL_READ, SC_SSL_WRITEV, SC_KEVENT,
SC_EPOLL_CREATE, SC_EPOLL_CTL, SC_EPOLL_WAIT,
SC_NUM_SYSCALLS
};
static const char *const syscall_name[SC_NUM_SYSCALLS] = {
"bind", "connct", "read", "select", "socket", "writev",
"ssl_read", "ssl_writev", "kevent",
"epoll_create", "epoll_ctl", "epoll_wait"
};
static Time syscall_time[SC_NUM_SYSCALLS];
static u_int syscall_count[SC_NUM_SYSCALLS];
#else
# define SYSCALL(n,s) \
{ \
do \
{ \
errno = 0; \
s; \
} \
while (errno == EINTR); \
}
#endif
struct hash_entry {
const char *hostname;
int port;
struct sockaddr_in sin;
} hash_table[HASH_TABLE_SIZE];
static int
hash_code(const char *server, size_t server_len, int port)
{
u_char *cp = (u_char *) server;
u_long h = port;
u_long g;
int ch;
/*
* Basically the ELF hash algorithm:
*/
while ((ch = *cp++) != '\0') {
h = (h << 4) + ch;
if ((g = (h & 0xf0000000)) != 0) {
h ^= g >> 24;
h &= ~g;
}
}
return h;
}
static struct hash_entry *
hash_enter(const char *server, size_t server_len, int port,
struct sockaddr_in *sin)
{
struct hash_entry *he;
int index =
hash_code(server, server_len, port) % HASH_TABLE_SIZE;
while (hash_table[index].hostname) {
++index;
if (index >= HASH_TABLE_SIZE)
index = 0;
}
he = hash_table + index;
he->hostname = server;
he->port = port;
he->sin = *sin;
return he;
}
static struct sockaddr_in *
hash_lookup(const char *server, size_t server_len, int port)
{
int index, start_index;
index = start_index =
hash_code(server, server_len, port) % HASH_TABLE_SIZE;
while (hash_table[index].hostname) {
if (hash_table[index].port == port
&& strcmp(hash_table[index].hostname, server) == 0)
return &hash_table[index].sin;
++index;
if (index >= HASH_TABLE_SIZE)
index = 0;
if (index == start_index)
break;
}
return 0;
}
static int
lffs(long w)
{
#ifdef __FreeBSD__
return ffsl(w);
#else
int r;
if (sizeof(w) == sizeof(int))
r = ffs(w);
else {
r = ffs(w);
#if SIZEOF_LONG > 4
if (r == 0) {
r = ffs(w >> (8 * sizeof(int)));
if (r > 0)
r += 8 * sizeof(int);
}
#endif
}
return r;
#endif
}
static void
port_put(struct local_addr *addr, int port)
{
int i, bit;
port -= MIN_IP_PORT;
i = port / BITSPERLONG;
bit = port % BITSPERLONG;
addr->port_free_map[i] |= (1UL << bit);
}
static int
port_get(struct local_addr *addr)
{
int port, bit, i;
i = addr->previous;
if ((addr->port_free_map[i] & addr->mask) == 0) {
do {
++i;
if (i >= NELEMS(addr->port_free_map))
i = 0;
if (i == addr->previous) {
if (DBG > 0)
fprintf(stderr,
"%s.port_get: Yikes! I'm out of port numbers!\n",
prog_name);
return -1;
}
}
while (addr->port_free_map[i] == 0);
addr->mask = ~0UL;
}
addr->previous = i;
bit = lffs(addr->port_free_map[i] & addr->mask) - 1;
if (bit >= BITSPERLONG - 1)
addr->mask = 0;
else
addr->mask = ~((1UL << (bit + 1)) - 1);
addr->port_free_map[i] &= ~(1UL << bit);
port = bit + i * BITSPERLONG + MIN_IP_PORT;
return port;
}
static void
conn_failure(Conn * s, int err)
{
Any_Type arg;
arg.l = err;
event_signal(EV_CONN_FAILED, (Object *) s, arg);
core_close(s);
}
static void
conn_timeout(struct Timer *t, Any_Type arg)
{
Conn *s = arg.vp;
Time now;
Call *c;
assert(object_is_conn(s));
s->watchdog = 0;
if (DBG > 0) {
c = 0;
if (s->sd >= 0) {
now = timer_now();
if (s->reading
&& s->recvq && now >= s->recvq->timeout)
c = s->recvq;
else if (s->writing
&& s->sendq && now >= s->sendq->timeout)
c = s->sendq;
}
if (DBG > 0) {
fprintf(stderr, "connection_timeout");
if (c)
fprintf(stderr, ".%lu", c->id);
fprintf(stderr, ": t=%p, connection=%p\n", t, s);
}
}
arg.l = 0;
event_signal(EV_CONN_TIMEOUT, (Object *) s, arg);
core_close(s);
}
enum IO_DIR { READ, WRITE };
static void
clear_active(Conn * s, enum IO_DIR dir)
{
int sd = s->sd;
#ifdef HAVE_KEVENT
struct kevent ev;
EV_SET(&ev, sd, dir == WRITE ? EVFILT_WRITE : EVFILT_READ, EV_DELETE,
0, 0, s);
if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) {
fprintf(stderr, "failed to add %s filter\n", write ?
"write" : "read");
exit(1);
}
#else
#ifdef HAVE_EPOLL
struct epoll_event ev;
int error;
if (dir == WRITE)
ev.events = EPOLLIN;
else
ev.events = EPOLLOUT;
ev.data.ptr = s;
error = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, sd, &ev);
if (error < 0) {
error = errno;
fprintf(stderr, "failed to EPOLL_CTL_DEL\n");
exit(1);
}
#else
fd_set * fdset;
if (dir == WRITE)
fdset = &wrfds;
else
fdset = &rdfds;
FD_CLR(sd, fdset);
#endif
#endif
if (dir == WRITE)
s->writing = 0;
else
s->reading = 0;
}
static void
set_active(Conn * s, enum IO_DIR dir)
{
int sd = s->sd;
Any_Type arg;
Time timeout;
#ifdef HAVE_KEVENT
struct kevent ev;
EV_SET(&ev, sd, dir == WRITE ? EVFILT_WRITE : EVFILT_READ, EV_ADD,
0, 0, s);
if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) {
fprintf(stderr, "failed to add %s filter\n", write ?
"write" : "read");
exit(1);
}
#else
#ifdef HAVE_EPOLL
struct epoll_event ev;
int error;
if (dir == WRITE)
ev.events = EPOLLOUT;
else
ev.events = EPOLLIN;
ev.data.ptr = s;
if (s->epoll_added)
error = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, sd, &ev);
else {
error = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sd, &ev);
s->epoll_added = 1;
}
if (error < 0) {
error = errno;
fprintf(stderr, "failed to EPOLL_CTL_MOD\n");
exit(1);
}
#else
fd_set * fdset;
if (dir == WRITE)
fdset = &wrfds;
else
fdset = &rdfds;
FD_SET(sd, fdset);
if (sd < min_sd)
min_sd = sd;
#endif
#endif
if (sd >= max_sd)
max_sd = sd;
if (dir == WRITE)
s->writing = 1;
else
s->reading = 1;
if (s->watchdog)
return;
timeout = 0.0;
if (s->sendq)
timeout = s->sendq->timeout;
if (s->recvq && (timeout == 0.0 || timeout > s->recvq->timeout))
timeout = s->recvq->timeout;
if (timeout > 0.0) {
arg.vp = s;
s->watchdog = timer_schedule(conn_timeout, arg,
timeout - timer_now());
}
}
static void
do_send(Conn * conn)
{
int async_errno;
socklen_t len;
struct iovec *iovp;
int sd = conn->sd;
ssize_t nsent = 0;
Any_Type arg;
Call *call;
while (1) {
call = conn->sendq;
assert(call);
arg.l = 0;
event_signal(EV_CALL_SEND_RAW_DATA, (Object *) call, arg);
#ifdef HAVE_SSL
if (param.use_ssl) {
extern ssize_t SSL_writev(SSL *, const struct iovec *,
int);
SYSCALL(SSL_WRITEV, nsent =
SSL_writev(conn->ssl,
call->req.iov + call->req.iov_index,
(NELEMS(call->req.iov)
- call->req.iov_index)));
} else
#endif
{
SYSCALL(WRITEV,
nsent =
writev(sd, call->req.iov + call->req.iov_index,
(NELEMS(call->req.iov)
- call->req.iov_index)));
}
if (DBG > 0)
fprintf(stderr, "do_send.%lu: wrote %ld bytes on %p\n",
call->id, (long) nsent, conn);
if (nsent < 0) {
if (errno == EAGAIN)
return;
len = sizeof(async_errno);
if (getsockopt
(sd, SOL_SOCKET, SO_ERROR, &async_errno, &len) == 0
&& async_errno != 0)
errno = async_errno;
if (DBG > 0)
fprintf(stderr,
"%s.do_send: writev() failed: %s\n",
prog_name, strerror(errno));
conn_failure(conn, errno);
return;
}
call->req.size += nsent;
iovp = call->req.iov + call->req.iov_index;
while (iovp < call->req.iov + NELEMS(call->req.iov)) {
if (nsent < iovp->iov_len) {
iovp->iov_len -= nsent;
iovp->iov_base =
(caddr_t) ((char *) iovp->iov_base +
nsent);
break;
} else {
/*
* we're done with this fragment:
*/
nsent -= iovp->iov_len;
*iovp = call->req.iov_saved;
++iovp;
call->req.iov_saved = *iovp;
}
}
call->req.iov_index = iovp - call->req.iov;
if (call->req.iov_index < NELEMS(call->req.iov)) {
/*
* there are more header bytes to write
*/
call->timeout =
param.timeout ? timer_now() + param.timeout : 0.0;
set_active(conn, WRITE);
return;
}
/*
* we're done with sending this request
*/
conn->sendq = call->sendq_next;
if (!conn->sendq) {
conn->sendq_tail = 0;
clear_active(conn, WRITE);
}
arg.l = 0;
event_signal(EV_CALL_SEND_STOP, (Object *) call, arg);
if (conn->state >= S_CLOSING) {
call_dec_ref(call);
return;
}
/*
* get ready to receive matching reply (note that we
* implicitly pass on the reference to the call from the sendq
* to the recvq):
*/
call->recvq_next = 0;
if (!conn->recvq)
conn->recvq = conn->recvq_tail = call;
else {
conn->recvq_tail->recvq_next = call;
conn->recvq_tail = call;
}
call->timeout = param.timeout + param.think_timeout;
if (call->timeout > 0.0)
call->timeout += timer_now();
set_active(conn, READ);
if (conn->state < S_REPLY_STATUS)
conn->state = S_REPLY_STATUS; /* expecting reply
* status */
if (!conn->sendq)
return;
arg.l = 0;
event_signal(EV_CALL_SEND_START, (Object *) conn->sendq, arg);
if (conn->state >= S_CLOSING)
return;
}
}
static void
recv_done(Call * call)
{
Conn *conn = call->conn;
Any_Type arg;
conn->recvq = call->recvq_next;
if (!conn->recvq) {
clear_active(conn, READ);
conn->recvq_tail = 0;
}
/*
* we're done with receiving this request
*/
arg.l = 0;
event_signal(EV_CALL_RECV_STOP, (Object *) call, arg);
call_dec_ref(call);
}
static void
do_recv(Conn * s)
{
char *cp, buf[8193];
Call *c = s->recvq;
int i, saved_errno;
ssize_t nread = 0;
size_t buf_len;
assert(c);
#ifdef HAVE_SSL
if (param.use_ssl) {
SYSCALL(SSL_READ,
nread = SSL_read(s->ssl, buf, sizeof(buf) - 1));
} else
#endif
{
SYSCALL(READ, nread = read(s->sd, buf, sizeof(buf) - 1));
}
saved_errno = errno;
if (nread <= 0) {
if (DBG > 0) {
fprintf(stderr,
"do_recv.%lu: received %lu reply bytes on %p\n",
c->id,
(u_long) (c->reply.header_bytes +
c->reply.content_bytes), s);
if (nread < 0)
fprintf(stderr,
"%s.do_recv: read() failed: %s\n",
prog_name, strerror(saved_errno));
}
if (nread < 0) {
if (saved_errno != EAGAIN)
conn_failure(s, saved_errno);
} else if (s->state != S_REPLY_DATA)
conn_failure(s, ECONNRESET);
else {
if (s->state < S_CLOSING)
s->state = S_REPLY_DONE;
recv_done(c);
}
return;
}
buf[nread] = '\0'; /* ensure buffer is '\0' terminated */
if (DBG > 3) {
/*
* dump received data in hex & ascii:
*/
fprintf(stderr, "do_recv.%lu: received reply data:\n", c->id);
for (cp = buf; cp < buf + nread;) {
fprintf(stderr, " %04x:",
(int) (c->reply.header_bytes +
c->reply.content_bytes + (cp - buf)));
for (i = 0; i < 16 && i < buf + nread - cp; ++i)
fprintf(stderr, " %02x", cp[i] & 0xff);
i *= 3;
while (i++ < 50)
fputc(' ', stderr);
for (i = 0; i < 16 && cp < buf + nread; ++i, ++cp)
fprintf(stderr, "%c",
isprint(*cp) ? *cp : '.');
fprintf(stderr, "\n");
}
}
/*
* process the replies in this buffer:
*/
buf_len = nread;
cp = buf;
do {
c = s->recvq;
assert(c);
/*
* sets right start time, but doesn't update each packet
*/
if (s->state == S_REPLY_STATUS) {
c->timeout = param.timeout + param.think_timeout;
if (c->timeout > 0.0)
c->timeout += timer_now();
}
http_process_reply_bytes(c, &cp, &buf_len);
if (s->state == S_REPLY_DONE) {
recv_done(c);
if (s->state >= S_CLOSING)
return;
s->state = S_REPLY_STATUS;
}
}
while (buf_len > 0);
if (s->recvq)
set_active(c->conn, READ);
}
struct sockaddr_in *
core_addr_intern(const char *server, size_t server_len, int port)
{
struct sockaddr_in sin;
struct hash_entry *h;
struct hostent *he;
Any_Type arg;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
arg.cvp = server;
event_signal(EV_HOSTNAME_LOOKUP_START, 0, arg);
he = gethostbyname(server);
event_signal(EV_HOSTNAME_LOOKUP_STOP, 0, arg);
if (he) {
if (he->h_addrtype != AF_INET
|| he->h_length != sizeof(sin.sin_addr)) {
fprintf(stderr,
"%s: can't deal with addr family %d or size %d\n",
prog_name, he->h_addrtype, he->h_length);
exit(1);
}
memcpy(&sin.sin_addr, he->h_addr_list[0],
sizeof(sin.sin_addr));
} else {
if (!inet_aton(server, &sin.sin_addr)) {
fprintf(stderr,
"%s.core_addr_intern: invalid server address %s\n",
prog_name, server);
exit(1);
}
}
h = hash_enter(server, server_len, port, &sin);
if (!h)
return 0;
return &h->sin;
}
static void
core_add_address(struct in_addr ip)
{
struct local_addr *addr;
myaddrs.addresses = realloc(myaddrs.addresses,
sizeof(struct local_addr) * (myaddrs.count + 1));
if (myaddrs.addresses == NULL) {
fprintf(stderr,
"%s: out of memory parsing address list\n",
prog_name);
exit(1);
}
addr = &myaddrs.addresses[myaddrs.count];
addr->ip = ip;
memset(&addr->port_free_map, 0xff, sizeof(addr->port_free_map));
addr->mask = ~0UL;
addr->previous = 0;
myaddrs.count++;
}
/*
* Parses the value provided to --myaddr. A value can either be a
* hostname or IP, or an IP range. Multiple values can be specified
* in which case all matches are added to a pool which new connections
* use in a round-robin fashion. An interface name may also be
* specified in which case all IP addresses assigned to that interface
* are used.
*/
void
core_add_addresses(const char *spec)
{
struct hostent *he;
struct in_addr ip;
#ifdef __FreeBSD__
struct ifaddrs *iflist, *ifa;
#endif
char *cp;
/* First try to resolve the argument as a hostname. */
he = gethostbyname(spec);
if (he) {
if (he->h_addrtype != AF_INET ||
he->h_length != sizeof(struct in_addr)) {
fprintf(stderr,
"%s: can't deal with addr family %d or size %d\n",
prog_name, he->h_addrtype, he->h_length);
exit(1);
}
core_add_address(*(struct in_addr *)he->h_addr_list[0]);
return;
}
/* If there seems to be an IP range, try that next. */
cp = strchr(spec, '-');
if (cp != NULL) {
char *start_s;
struct in_addr end_ip;
start_s = strndup(spec, cp - spec);
if (!inet_aton(start_s, &ip)) {
fprintf(stderr, "%s: invalid starting address %s\n",
prog_name, start_s);
exit(1);
}
if (!inet_aton(cp + 1, &end_ip)) {
fprintf(stderr, "%s: invalid ending address %s\n",
prog_name, cp + 1);
exit(1);
}
while (ip.s_addr != end_ip.s_addr) {
core_add_address(ip);
ip.s_addr += htonl(1);
}
core_add_address(end_ip);
return;
}
/* Check for a single IP. */
if (inet_aton(spec, &ip)) {
core_add_address(ip);
return;
}
#ifdef __FreeBSD__
/* Check for an interface name. */
if (getifaddrs(&iflist) == 0) {
int found;
found = 0;
for (ifa = iflist; ifa != NULL; ifa = ifa->ifa_next) {
if (strcmp(ifa->ifa_name, spec) != 0)
continue;
if (found == 0)
found = 1;
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
found = 2;
core_add_address(
((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
}
freeifaddrs(iflist);
if (found == 2)
return;
if (found == 1) {
fprintf(stderr,
"%s: no valid addresses found on interface %s\n",
prog_name, spec);
exit(1);
}
}
#endif
fprintf(stderr, "%s: invalid address list %s\n",
prog_name, spec);
exit(1);
}
static struct local_addr *
core_get_next_myaddr(void)
{
struct local_addr *addr;
assert(myaddrs.last >= 0 && myaddrs.last < myaddrs.count);
addr = &myaddrs.addresses[myaddrs.last];
myaddrs.last++;
if (myaddrs.last == myaddrs.count)
myaddrs.last = 0;
return (addr);
}
static void
core_runtime_timer(struct Timer *t, Any_Type arg)
{
core_exit();
}
void
core_init(void)
{
struct rlimit rlimit;
Any_Type arg;
memset(&hash_table, 0, sizeof(hash_table));
#if !defined(HAVE_KEVENT) && !defined(HAVE_EPOLL)
memset(&rdfds, 0, sizeof(rdfds));
memset(&wrfds, 0, sizeof(wrfds));
#endif
memset(&myaddr, 0, sizeof(myaddr));
#ifdef __FreeBSD__
myaddr.sin_len = sizeof(myaddr);
#endif
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (myaddrs.count == 0)
core_add_address(myaddr.sin_addr);
/*
* Don't disturb just because a TCP connection closed on us...
*/
signal(SIGPIPE, SIG_IGN);
#ifdef HAVE_KEVENT
kq = kqueue();
if (kq < 0) {
fprintf(stderr,
"%s: failed to create kqueue: %s", prog_name,
strerror(errno));
exit(1);
}
/*
* TIMER_INTERVAL doesn't exist anymore, so just take a wild
* guess.
*/
struct kevent ev;
EV_SET(&ev, 0, EVFILT_TIMER, EV_ADD, NOTE_MSECONDS, 1, NULL);
if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) {
fprintf(stderr,
"%s: failed to add timer event: %s", prog_name,
strerror(errno));
}
#else
#ifdef HAVE_EPOLL
epoll_fd = epoll_create(EPOLL_N_MAX);
if (epoll_fd < 0) {
fprintf(stderr,
"%s: failed to create epoll: %s", prog_name,
strerror(errno));
exit(1);
}
epoll_events = calloc(EPOLL_N_MAX, sizeof(struct epoll_event));
if (epoll_events == NULL) {
fprintf(stderr,
"%s: failed to create epoll_events: %s", prog_name,
strerror(errno));
exit(1);
}
epoll_timeout = 0;
#else
#ifdef DONT_POLL
/*
* This causes select() to take several milliseconds on both Linux/x86
* and HP-UX 10.20.
*/
select_timeout.tv_sec = (u_long) TIMER_INTERVAL;
select_timeout.tv_usec = (u_long) (TIMER_INTERVAL * 1e6);
#else
/*
* This causes httperf to become a CPU hog as it polls for
* filedescriptors to become readable/writable. This is OK as long as
* httperf is the only (interesting) user-level process that executes
* on a machine.
*/
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 0;
#endif
#endif
#endif
/*
* boost open file limit to the max:
*/
if (getrlimit(RLIMIT_NOFILE, &rlimit) < 0) {
fprintf(stderr,
"%s: failed to get number of open file limit: %s",
prog_name, strerror(errno));
exit(1);
}
rlimit.rlim_cur = rlimit.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rlimit) < 0) {
fprintf(stderr,
"%s: failed to increase number of open file limit: %s",
prog_name, strerror(errno));
exit(1);
}
if (verbose)
printf("%s: maximum number of open descriptors = %ld\n",
prog_name, rlimit.rlim_max);
if (param.servers)
conn_add_servers();
else if (param.server)
core_addr_intern(param.server, strlen(param.server), param.port);
if (param.runtime) {
arg.l = 0;
timer_schedule(core_runtime_timer, arg, param.runtime);
}
}
#ifdef HAVE_SSL
void
core_ssl_connect(Conn * s)
{
Any_Type arg;
int ssl_err;
if (DBG > 2)
fprintf(stderr, "core_ssl_connect(conn=%p)\n", (void *) s);
if (SSL_set_fd(s->ssl, s->sd) == 0) {
ERR_print_errors_fp(stderr);
exit(-1);
}
ssl_err = SSL_connect(s->ssl);
if (ssl_err < 0) {
int reason = SSL_get_error(s->ssl, ssl_err);
if (reason == SSL_ERROR_WANT_READ
|| reason == SSL_ERROR_WANT_WRITE) {
if (DBG > 2)
fprintf(stderr,
"core_ssl_connect: want to %s more...\n",
(reason ==
SSL_ERROR_WANT_READ) ? "read" :
"write");
if (reason == SSL_ERROR_WANT_READ
&& !s->reading) {
clear_active(s, WRITE);
set_active(s, READ);
} else if (reason == SSL_ERROR_WANT_WRITE
&& !s->writing) {
clear_active(s, READ);
set_active(s, WRITE);
}
return;
}
fprintf(stderr,
"%s: failed to connect to SSL server (err=%d, reason=%d)\n",
prog_name, ssl_err, reason);
ERR_print_errors_fp(stderr);
exit(-1);
}
s->state = S_CONNECTED;
if (DBG > 0)
fprintf(stderr, "core_ssl_connect: SSL is connected!\n");
if (DBG > 1) {
const SSL_CIPHER *ssl_cipher;
ssl_cipher = SSL_get_current_cipher(s->ssl);
if (!ssl_cipher)
fprintf(stderr,
"core_ssl_connect: server refused all client cipher "
"suites!\n");
else
fprintf(stderr,
"core_ssl_connect: cipher=%s, id=%lu\n",
SSL_CIPHER_get_name(ssl_cipher),
SSL_CIPHER_get_id(ssl_cipher));
}
arg.l = 0;
event_signal(EV_CONN_CONNECTED, (Object *) s, arg);
}
#endif /* HAVE_SSL */
int
core_connect(Conn * s)
{
int sd, result, async_errno;
socklen_t len;
struct sockaddr_in *sin;
struct linger linger;
int myport, optval;
Any_Type arg;
static int prev_iteration = -1;
static u_long burst_len;
if (iteration == prev_iteration)
++burst_len;
else {
if (burst_len > max_burst_len)
max_burst_len = burst_len;
burst_len = 1;
prev_iteration = iteration;
}
SYSCALL(SOCKET, sd = socket(AF_INET, SOCK_STREAM, 0));
if (sd < 0) {
if (DBG > 0)
fprintf(stderr,
"%s.core_connect.socket: %s (max_sd=%d)\n",
prog_name, strerror(errno), max_sd);
goto failure;
}
if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) {
fprintf(stderr, "%s.core_connect.fcntl: %s\n",
prog_name, strerror(errno));
goto failure;
}
if (param.close_with_reset) {
linger.l_onoff = 1;
linger.l_linger = 0;
if (setsockopt
(sd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) < 0) {
fprintf(stderr,
"%s.core_connect.setsockopt(SO_LINGER): %s\n",
prog_name, strerror(errno));
goto failure;
}
}
/*
* Disable Nagle algorithm so we don't delay needlessly when
* pipelining requests.
*/
optval = 1;
if (setsockopt(sd, SOL_TCP, TCP_NODELAY, &optval, sizeof(optval)) < 0) {
fprintf(stderr, "%s.core_connect.setsockopt(SO_SNDBUF): %s\n",
prog_name, strerror(errno));
goto failure;
}
optval = param.send_buffer_size;
if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) < 0) {
fprintf(stderr, "%s.core_connect.setsockopt(SO_SNDBUF): %s\n",
prog_name, strerror(errno));
goto failure;
}
optval = param.recv_buffer_size;
if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) < 0) {
fprintf(stderr, "%s.core_connect.setsockopt(SO_SNDBUF): %s\n",
prog_name, strerror(errno));
goto failure;
}
s->sd = sd;
#if !defined(HAVE_KEVENT) && !defined(HAVE_EPOLL)
if (sd >= alloced_sd_to_conn) {
size_t size, old_size;
old_size = alloced_sd_to_conn * sizeof(sd_to_conn[0]);
alloced_sd_to_conn += 2048;
size = alloced_sd_to_conn * sizeof(sd_to_conn[0]);
if (sd_to_conn)
sd_to_conn = realloc(sd_to_conn, size);
else
sd_to_conn = malloc(size);
if (!sd_to_conn) {
if (DBG > 0)
fprintf(stderr,
"%s.core_connect.realloc: %s\n",
prog_name, strerror(errno));
goto failure;
}
memset((char *) sd_to_conn + old_size, 0, size - old_size);
}
assert(!sd_to_conn[sd]);
sd_to_conn[sd] = s;
#endif
sin = hash_lookup(s->hostname, s->hostname_len, s->port);
if (!sin) {
if (DBG > 0)
fprintf(stderr,
"%s.core_connect: unknown server/port %s:%d\n",
prog_name, s->hostname, s->port);
goto failure;
}
arg.l = 0;
event_signal(EV_CONN_CONNECTING, (Object *) s, arg);
if (s->state >= S_CLOSING)
goto failure;
s->myaddr = core_get_next_myaddr();
myaddr.sin_addr = s->myaddr->ip;
if (param.hog) {
while (1) {
myport = port_get(s->myaddr);
if (myport < 0)
goto failure;
myaddr.sin_port = htons(myport);
SYSCALL(BIND,
result = bind(sd, (struct sockaddr *) &myaddr,
sizeof(myaddr)));
if (result == 0)
break;
if (errno != EADDRINUSE && errno == EADDRNOTAVAIL) {
if (DBG > 0)
fprintf(stderr,
"%s.core_connect.bind: %s\n",
prog_name, strerror(errno));
goto failure;
}
}
s->myport = myport;
} else if (myaddr.sin_addr.s_addr != htonl(INADDR_ANY)) {
SYSCALL(BIND,
result = bind(sd, (struct sockaddr *) &myaddr,
sizeof(myaddr)));
if (result != 0)
goto failure;
}
SYSCALL(CONNECT,
result = connect(sd, (struct sockaddr *) sin, sizeof(*sin)));
if (result == 0) {
#ifdef HAVE_SSL
if (param.use_ssl)
core_ssl_connect(s);
else
#endif
{
s->state = S_CONNECTED;
arg.l = 0;
event_signal(EV_CONN_CONNECTED, (Object *) s, arg);
}
} else if (errno == EINPROGRESS) {
/*
* The socket becomes writable only after the connection has
* been established. Hence we wait for writability to detect
* connection establishment.
*/
s->state = S_CONNECTING;
set_active(s, WRITE);
if (param.timeout > 0.0) {
arg.vp = s;
assert(!s->watchdog);
s->watchdog =
timer_schedule(conn_timeout, arg, param.timeout);
}
} else {
len = sizeof(async_errno);
if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &async_errno, &len) ==
0 && async_errno != 0)
errno = async_errno;
if (DBG > 0)
fprintf(stderr,
"%s.core_connect.connect: %s (max_sd=%d)\n",
prog_name, strerror(errno), max_sd);
if (s->myport > 0)
port_put(s->myaddr, s->myport);
goto failure;
}
return 0;
failure:
conn_failure(s, errno);
return -1;
}
int
core_send(Conn * conn, Call * call)
{
Any_Type arg;
arg.l = 0;
event_signal(EV_CALL_ISSUE, (Object *) call, arg);
call->conn = conn; /* NO refcounting here (see call.h). */
if (param.no_host_hdr) {
call->req.iov[IE_HOST].iov_base = (caddr_t) "";
call->req.iov[IE_HOST].iov_len = 0;
} else if (!call->req.iov[IE_HOST].iov_base) {
/*
* Default call's hostname to connection's hostname:
*/
call->req.iov[IE_HOST].iov_base = (caddr_t) conn->fqdname;
call->req.iov[IE_HOST].iov_len = conn->fqdname_len;
}
/*
* NOTE: the protocol version indicates what the _client_ can
* understand. If we send HTTP/1.1, it doesn't mean that the server
* has to speak HTTP/1.1. In other words, sending an HTTP/1.1 header
* leaves it up to the server whether it wants to reply with a 1.0 or
* 1.1 reply.
*/
switch (call->req.version) {
case 0x10000:
if (param.no_host_hdr) {
call->req.iov[IE_PROTL].iov_base =
(caddr_t) http10req_nohost;
call->req.iov[IE_PROTL].iov_len =
sizeof(http10req_nohost) - 1;
} else {
call->req.iov[IE_PROTL].iov_base = (caddr_t) http10req;
call->req.iov[IE_PROTL].iov_len =
sizeof(http10req) - 1;
}
break;
case 0x10001:
if (param.no_host_hdr) {
call->req.iov[IE_PROTL].iov_base = http11req_nohost;
call->req.iov[IE_PROTL].iov_len =
sizeof(http11req_nohost) - 1;
} else {
call->req.iov[IE_PROTL].iov_base = http11req;
call->req.iov[IE_PROTL].iov_len =
sizeof(http11req) - 1;
}
break;
default:
fprintf(stderr, "%s: unexpected version code %x\n",
prog_name, call->req.version);
exit(1);
}
call->req.iov_index = 0;
call->req.iov_saved = call->req.iov[0];
/*
* insert call into connection's send queue:
*/
call_inc_ref(call);
call->sendq_next = 0;
if (!conn->sendq) {
conn->sendq = conn->sendq_tail = call;
arg.l = 0;
event_signal(EV_CALL_SEND_START, (Object *) call, arg);
if (conn->state >= S_CLOSING)
return -1;
call->timeout =
param.timeout ? timer_now() + param.timeout : 0.0;
set_active(conn, WRITE);
} else {
conn->sendq_tail->sendq_next = call;
conn->sendq_tail = call;
}
return 0;
}
void
core_close(Conn * conn)
{
Call *call, *call_next;
Any_Type arg;
int sd;
if (conn->state >= S_CLOSING)
return; /* guard against recursive calls */
conn->state = S_CLOSING;
if (DBG >= 10)
fprintf(stderr, "%s.core_close(conn=%p)\n", prog_name, conn);
if (conn->watchdog) {
timer_cancel(conn->watchdog);
conn->watchdog = 0;
}
/*
* first, get rid of all pending calls:
*/
for (call = conn->sendq; call; call = call_next) {
call_next = call->sendq_next;
call_dec_ref(call);
}
conn->sendq = 0;
for (call = conn->recvq; call; call = call_next) {
call_next = call->recvq_next;
call_dec_ref(call);
}
conn->recvq = 0;
sd = conn->sd;
conn->sd = -1;
arg.l = 0;
event_signal(EV_CONN_CLOSE, (Object *) conn, arg);
assert(conn->state == S_CLOSING);
#ifdef HAVE_SSL
if (param.use_ssl)
SSL_shutdown(conn->ssl);
#endif
if (sd >= 0) {
#ifdef HAVE_EPOLL
struct epoll_event ev = { 0, { 0 } };
int error;
error = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sd, &ev);
if (error < 0) {
if (conn->epoll_added == 1 && error != ENOENT) {
error = errno;
printf("EPOLL_CTL_DEL: %d %d %d\n", epoll_fd, sd, error);
assert(error == 0);
}
}
#endif
close(sd);
#if !defined(HAVE_KEVENT) && !defined(HAVE_EPOLL)
sd_to_conn[sd] = 0;
FD_CLR(sd, &wrfds);
FD_CLR(sd, &rdfds);
#endif
conn->reading = 0;
conn->writing = 0;
}
if (conn->myport > 0)
port_put(conn->myaddr, conn->myport);
/*
* A connection that has been closed is not useful anymore, so we give
* up the reference obtained when creating the session. This normally
* initiates destruction of the connection.
*/
conn_dec_ref(conn);
}
#ifdef HAVE_KEVENT
void
core_loop(void)
{
struct kevent ev;
int n;
Any_Type arg;
Conn *conn;
while (running) {
++iteration;
n = kevent(kq, NULL, 0, &ev, 1, NULL);
if (n < 0 && errno != EINTR) {
fprintf(stderr, "failed to fetch event: %s",
strerror(errno));
exit(1);
}
switch (ev.filter) {
case EVFILT_TIMER:
timer_tick();
break;
case EVFILT_READ:
case EVFILT_WRITE:
conn = ev.udata;
conn_inc_ref(conn);
if (conn->watchdog) {
timer_cancel(conn->watchdog);
conn->watchdog = 0;
}
if (conn->state == S_CONNECTING) {
#ifdef HAVE_SSL
if (param.use_ssl)
core_ssl_connect(conn);
else
#endif
if (ev.filter == EVFILT_WRITE) {
clear_active(conn, WRITE);
conn->state = S_CONNECTED;
arg.l = 0;
event_signal(EV_CONN_CONNECTED, (Object*)conn, arg);
}
} else {
if (ev.filter == EVFILT_WRITE && conn->sendq)
do_send(conn);
if (ev.filter == EVFILT_READ && conn->recvq)
do_recv(conn);
}
conn_dec_ref(conn);
break;
}
}
}
#else
#ifdef HAVE_EPOLL
void
core_loop(void)
{
struct epoll_event *ep;
int i, n;
Any_Type arg;
Conn *conn;
while (running) {
++iteration;
timer_tick();
n = epoll_wait(epoll_fd, epoll_events, EPOLL_N_MAX, epoll_timeout);
if (n < 0 && errno == EINTR) {
fprintf(stderr, "failed to fetch event: %s",
strerror(errno));
continue;
}
ep = epoll_events;
for (i = 0; i < n; i++, ep++) {
conn = ep->data.ptr;
conn_inc_ref(conn);
if (conn->watchdog) {
timer_cancel(conn->watchdog);
conn->watchdog = 0;
}
if (conn->state == S_CONNECTING) {
#ifdef HAVE_SSL
if (param.use_ssl)
core_ssl_connect(conn);
else
#endif
if (ep->events & EPOLLOUT) {
clear_active(conn, WRITE);
conn->state = S_CONNECTED;
arg.l = 0;
event_signal(EV_CONN_CONNECTED, (Object*)conn, arg);
}
} else {
if (ep->events & (EPOLLIN | EPOLLHUP) && conn->recvq)
do_recv(conn);
if (ep->events & EPOLLOUT && conn->sendq)
do_send(conn);
}
conn_dec_ref(conn);
}
}
close(epoll_fd);
}
#else
void
core_loop(void)
{
int is_readable, is_writable, n, sd, bit, min_i, max_i, i = 0;
fd_set readable, writable;
fd_mask mask;
Any_Type arg;
Conn *conn;
while (running) {
struct timeval tv = select_timeout;
timer_tick();
readable = rdfds;
writable = wrfds;
min_i = min_sd / NFDBITS;
max_i = max_sd / NFDBITS;
SYSCALL(SELECT, n = select(max_sd + 1, &readable, &writable, 0, &tv));
++iteration;
if (n <= 0) {
if (n < 0) {
fprintf(stderr, "%s.core_loop: select failed: %s\n", prog_name, strerror(errno));
exit(1);
}
continue;
}
while (n > 0) {
/*
* find the index of the fdmask that has something
* going on:
*/
do {
++i;
if (i > max_i)
i = min_i;
assert(i <= max_i);
mask = readable.fds_bits[i] | writable.fds_bits[i];
} while (!mask);
bit = 0;
sd = i * NFDBITS + bit;
do {
if (mask & 1) {
--n;
is_readable = (FD_ISSET(sd, &readable) && FD_ISSET(sd, &rdfds));
is_writable = (FD_ISSET(sd, &writable) && FD_ISSET(sd, &wrfds));
if (is_readable || is_writable) {
/*
* only handle sockets that
* haven't timed out yet
*/
conn = sd_to_conn[sd];
conn_inc_ref(conn);
if (conn->watchdog) {
timer_cancel(conn->watchdog);
conn->watchdog = 0;
}
if (conn->state == S_CONNECTING) {
#ifdef HAVE_SSL
if (param.use_ssl)
core_ssl_connect(conn);
else
#endif
if (is_writable) {
clear_active(conn, WRITE);
conn->state = S_CONNECTED;
arg.l = 0;
event_signal(EV_CONN_CONNECTED, (Object*)conn, arg);
}
} else {
if (is_writable && conn->sendq)
do_send(conn);
if (is_readable && conn->recvq)
do_recv(conn);
}
conn_dec_ref(conn);
if (n > 0)
timer_tick();
}
}
mask = ((u_long) mask) >> 1;
++sd;
} while (mask);
}
}
}
#endif
#endif
void
core_exit(void)
{
running = 0;
param.num_conns = 0;
printf("Maximum connect burst length: %lu\n", max_burst_len);
#ifdef TIME_SYSCALLS
{
u_int count;
Time time;
int i;
printf("Average syscall execution times:\n");
for (i = 0; i < NELEMS(syscall_name); ++i) {
count = syscall_count[i];
time = syscall_time[i];
printf("\t%s:\t%.3f ms/call (%.3fs total, %u calls)\n",
syscall_name[i],
count > 0 ? 1e3 * time / count : 0, time,
count);
}
putchar('\n');
}
#endif
}
07070100000019000081ED0000000000000000000000015FCD0002000007E1000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/core.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef core_h
#define core_h
#include <netinet/in.h>
extern void core_init (void);
extern void core_add_addresses (const char *spec);
extern struct sockaddr_in *core_addr_intern (const char *hostname,
size_t hostname_len, int port);
extern int core_connect (Conn *conn);
extern int core_send (Conn *conn, Call *call);
extern void core_close (Conn *conn);
extern void core_loop (void);
extern void core_exit (void);
#endif /* core_h */
0707010000001A000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/src/gen0707010000001B000081ED0000000000000000000000015FCD00020000013C000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/Makefile.am# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(srcdir)/.. -I$(srcdir)/../lib
AM_LDFLAGS =
noinst_LIBRARIES = libgen.a
libgen_a_SOURCES = call_seq.c conn_rate.c misc.c rate.c rate.h session.c \
session.h uri_fixed.c uri_wlog.c uri_wset.c \
wsess.c wsesslog.c wsesspage.c \
sess_cookie.c
0707010000001C000081ED0000000000000000000000015FCD000200000F3A000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/call_seq.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Issue a sequence of calls on a connection. */
#include "config.h"
#include <assert.h>
#include <sys/types.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#define CONN_PRIVATE_DATA(c) \
((Conn_Private_Data *) ((char *)(c) + conn_private_data_offset))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
typedef struct Conn_Private_Data
{
int num_calls;
int num_completed;
int num_destroyed;
}
Conn_Private_Data;
static size_t conn_private_data_offset;
static void
issue_calls (Conn *conn)
{
Conn_Private_Data *priv;
Call *call;
int i;
priv = CONN_PRIVATE_DATA (conn);
priv->num_completed = 0;
priv->num_destroyed = 0;
for (i = 0; i < param.burst_len; ++i)
if (priv->num_calls++ < param.num_calls)
{
call = call_new ();
if (call)
{
core_send (conn, call);
call_dec_ref (call);
}
}
}
static void
conn_connected (Event_Type et, Conn *conn)
{
assert (et == EV_CONN_CONNECTED && object_is_conn (conn));
issue_calls (conn);
}
static void
call_done (Event_Type et, Call *call)
{
Conn *conn = call->conn;
Conn_Private_Data *priv;
assert (et == EV_CALL_RECV_STOP && conn && object_is_conn (conn));
priv = CONN_PRIVATE_DATA (conn);
++priv->num_completed;
}
static void
call_destroyed (Event_Type et, Call *call)
{
Conn_Private_Data *priv;
Conn *conn;
assert (et == EV_CALL_DESTROYED && object_is_call (call));
conn = call->conn;
priv = CONN_PRIVATE_DATA (conn);
if (++priv->num_destroyed >= MIN (param.burst_len, param.num_calls))
{
if (priv->num_completed == priv->num_destroyed
&& priv->num_calls < param.num_calls)
issue_calls (conn);
else
core_close (conn);
}
}
static void
init (void)
{
Any_Type arg;
conn_private_data_offset = object_expand (OBJ_CONN,
sizeof (Conn_Private_Data));
arg.l = 0;
event_register_handler (EV_CONN_CONNECTED, (Event_Handler) conn_connected,
arg);
event_register_handler (EV_CALL_RECV_STOP, (Event_Handler) call_done, arg);
event_register_handler (EV_CALL_DESTROYED, (Event_Handler) call_destroyed,
arg);
}
Load_Generator call_seq =
{
"performs a sequence of calls on a connection",
init,
no_op,
no_op
};
0707010000001D000081ED0000000000000000000000015FCD000200000BC1000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/conn_rate.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Creates connections at the fixed rate PARAM.RATE or sequentially if
PARAM.RATE is zero. */
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <localevent.h>
#include <rate.h>
#include <conn.h>
#include <call.h>
#include <core.h>
static int num_conns_generated;
static int num_conns_destroyed;
static int num_conns_open;
static Rate_Generator rg;
static bool paused;
static int
make_conn (Any_Type arg)
{
Conn *s;
if (paused)
return 0;
if (num_conns_generated++ >= param.num_conns)
return -1;
if (param.max_conns != 0 && num_conns_open >= param.max_conns)
return 0;
s = conn_new ();
if (!s)
return -1;
num_conns_open++;
if (core_connect (s) == -1) {
num_conns_generated--;
num_conns_destroyed--;
paused = true;
}
return 0;
}
static void
destroyed (void)
{
Any_Type arg;
if (++num_conns_destroyed >= param.num_conns)
core_exit ();
num_conns_open--;
paused = false;
}
static void
init (void)
{
Any_Type arg;
rg.arg.l = 0;
rg.tick = make_conn;
arg.l = 0;
event_register_handler (EV_CONN_DESTROYED, (Event_Handler) destroyed, arg);
}
static void
start (void)
{
rg.rate = ¶m.rate;
rate_generator_start (&rg, EV_CONN_DESTROYED);
}
Load_Generator conn_rate =
{
"creates connections at a fixed rate",
init,
start,
no_op
};
0707010000001E000081ED0000000000000000000000015FCD000200001299000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/gen/misc.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Implements miscellaneous command-line specified operations. So
far, the following options are implemented here:
--add-header Adds one or more command-line specified header(s)
to each call request.
--method Sets the method to be used when performing a
call. */
#include "config.h"
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <call.h>
#include <localevent.h>
static const char *extra, *extra_file;
static size_t extra_len;
static size_t method_len, file_len;
/* A simple module that collects cookies from the server responses and
includes them in future calls to the server. */
static const char *
unescape (const char *str, size_t *len)
{
char *dp, *dst = strdup (str);
const char *cp;
int ch;
if (!dst)
panic ("%s: strdup() failed: %s\n", prog_name, strerror (errno));
for (cp = str, dp = dst; (ch = *cp++); )
{
if (ch == '\\')
{
ch = *cp++;
switch (ch)
{
case '\\': /* \\ -> \ */
break;
case 'a': /* \a -> LF */
ch = 10;
break;
case 'r': /* \r -> CR */
ch = 13;
break;
case 'n': /* \n -> CR/LF */
*dp++ = 13;
ch = 10;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ch = strtol (cp - 1, (char **) &cp, 8);
break;
default:
fprintf (stderr, "%s: ignoring unknown escape sequence "
"`\\%c' in --add-header\n", prog_name, ch);
break;
}
}
*dp++ = ch;
}
*len = dp - dst;
return dst;
}
static void
call_created (Event_Type et, Object *obj, Any_Type reg_arg, Any_Type arg)
{
Call *c = (Call *) obj;
assert (et == EV_CALL_NEW && object_is_call (obj));
if (method_len > 0)
call_set_method (c, param.method, method_len);
if (extra_len > 0)
call_append_request_header (c, extra, extra_len);
if (file_len > 0)
call_append_request_header (c, extra_file, file_len);
}
static void
init (void)
{
Any_Type arg;
struct stat st;
int fd;
if (param.additional_header)
extra = unescape (param.additional_header, &extra_len);
if (param.additional_header_file) {
fd = open (param.additional_header_file, O_RDONLY);
if (fd < 0)
fprintf (stderr, "%s: failed to open header file\n", prog_name);
else {
if (fstat(fd, &st) < 0)
fprintf (stderr, "%s: failed to stat header file\n", prog_name);
else {
extra_file = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (extra_file == (char *)MAP_FAILED) {
fprintf (stderr, "%s: failed to map header file\n", prog_name);
extra_file = NULL;
} else
file_len = st.st_size;
}
close(fd);
}
}
if (param.method)
method_len = strlen (param.method);
arg.l = 0;
event_register_handler (EV_CALL_NEW, call_created, arg);
}
Load_Generator misc =
{
"Miscellaneous command line options",
init,
no_op,
no_op
};
0707010000001F000081ED0000000000000000000000015FCD000200001594000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/gen/rate.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#include "config.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <localevent.h>
#include <rate.h>
#include <timer.h>
int current_rate = 0;
Time duration_in_current_rate = 0;
/* By pushing the random number generator state into the caller via
the xsubi array below, we gain some test repeatability. For
example, let us say one generator was starting sessions, and a
different generator was controlling requests within a session. If
both processes were sharing the same random number generator, then
a premature session termination would change the subsequent session
arrival spacing. */
static Time
next_arrival_time_det (Rate_Generator *rg)
{
return rg->rate->mean_iat;
}
static Time
next_arrival_time_uniform (Rate_Generator *rg)
{
Time lower, upper;
lower = rg->rate->min_iat;
upper = rg->rate->max_iat;
return lower + (upper - lower)*erand48 (rg->xsubi);
}
static Time
next_arrival_time_exp (Rate_Generator *rg)
{
Time mean = rg->rate->mean_iat;
return -mean*log (1.0 - erand48 (rg->xsubi));
}
static Time
next_arrival_time_variable (Rate_Generator *rg)
{
Time next;
next = rg->rate->iat[current_rate];
duration_in_current_rate += next;
if (duration_in_current_rate >= rg->rate->duration[current_rate])
{
current_rate++;
if (current_rate >= rg->rate->numRates)
current_rate = 0;
duration_in_current_rate = 0;
}
return (next);
}
static void
tick (struct Timer *t, Any_Type arg)
{
Time delay, now = timer_now ();
Rate_Generator *rg = arg.vp;
rg->timer = 0;
if (rg->done)
return;
while (now > rg->next_time)
{
delay = (*rg->next_interarrival_time) (rg);
if (verbose > 2)
fprintf (stderr, "next arrival delay = %.4f\n", delay);
rg->next_time += delay;
rg->done = ((*rg->tick) (rg->arg) < 0);
if (rg->done)
return;
}
rg->timer = timer_schedule ((Timer_Callback) tick, arg, rg->next_time - now);
}
static void
done (Event_Type type, Object *obj, Any_Type reg_arg, Any_Type call_arg)
{
Rate_Generator *rg = reg_arg.vp;
if (rg->done)
return;
rg->done = ((*rg->tick) (rg->arg) < 0);
}
void
rate_generator_start (Rate_Generator *rg, Event_Type completion_event)
{
Time (*func) (struct Rate_Generator *rg);
Any_Type arg;
Time delay;
/* Initialize random number generator with the init values here, all
rate generators (although independent) will follow the same
sequence of random values. We factor in the client's id to make
sure no two machines running httperf generate identical random
numbers. May want to pass these values as args to
rate_generator_start in the future. */
rg->xsubi[0] = 0x1234 ^ param.client.id;
rg->xsubi[1] = 0x5678 ^ (param.client.id << 8);
rg->xsubi[2] = 0x9abc ^ ~param.client.id;
arg.vp = rg;
if (rg->rate->rate_param > 0.0)
{
switch (rg->rate->dist)
{
case DETERMINISTIC: func = next_arrival_time_det; break;
case UNIFORM: func = next_arrival_time_uniform; break;
case EXPONENTIAL: func = next_arrival_time_exp; break;
case VARIABLE: func = next_arrival_time_variable; break;
default:
fprintf (stderr, "%s: unrecognized interarrival distribution %d\n",
prog_name, rg->rate->dist);
exit (-1);
}
rg->next_interarrival_time = func;
delay = (*func) (rg);
/* bias `next time' so that timeouts are rounded to the closest
tick: */
rg->next_time = timer_now () + delay;
rg->timer = timer_schedule ((Timer_Callback) tick, arg, delay);
}
else
/* generate callbacks sequentially: */
event_register_handler (completion_event, done, arg);
rg->start = timer_now ();
rg->done = ((*rg->tick) (rg->arg) < 0);
}
void
rate_generator_stop (Rate_Generator *rg)
{
if (rg->timer)
{
timer_cancel (rg->timer);
rg->timer = 0;
}
rg->done = 1;
}
07070100000020000081ED0000000000000000000000015FCD000200000855000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/gen/rate.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef rate_h
#define rate_h
#include <httperf.h>
#include <timer.h>
typedef struct Rate_Generator
{
u_short xsubi[3]; /* used for random number generation */
Rate_Info *rate;
Time start;
Time next_time;
Any_Type arg;
struct Timer *timer;
int (*tick) (Any_Type arg);
int done;
Time (*next_interarrival_time) (struct Rate_Generator *rg);
}
Rate_Generator;
extern void rate_generator_start (Rate_Generator *rg,
Event_Type completion_event);
extern void rate_generator_stop (Rate_Generator *rg);
#endif /* rate_h */
07070100000021000081ED0000000000000000000000015FCD000200001868000000000000000000000000000000000000003100000000httperf-0.9.0+git.20201206/src/gen/sess_cookie.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* This module intercepts `Set-Cookie:' headers on a per-session basis
and includes set cookies in future calls of the session.
Missing features:
- intercepted cookies are always sent, independent of any constraints
that may be present in the set-cookie header
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <session.h>
#define MAX_COOKIE_LEN 256
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
#define CALL_PRIVATE_DATA(c) \
((Call_Private_Data *) ((char *)(c) + call_private_data_offset))
typedef struct Sess_Private_Data
{
/* For now, we support just one cookie per session. If we get
more than one cookie, we'll print a warning message when
--debug is turned on. */
size_t cookie_len;
/* We can't malloc the cookie string because we may get a
``Set-Cookie:'' while there are calls pending that refer to an
existing cookie. So if we were to malloc & free cookies, we
would have to use reference counting to avoid the risk of
dangling pointers. */
char cookie[MAX_COOKIE_LEN];
}
Sess_Private_Data;
/* We need the call private data to ensure that the cookie gets set
only once. EV_CALL_ISSUE gets signalled each time a call is sent
on a connection. Since a connection may fail, the same call may be
issued multiple times, hence we need to make sure that the cookie
gets set only once per call. */
typedef struct Call_Private_Data
{
u_int cookie_present; /* non-zero if cookie has been set already */
char cookie[MAX_COOKIE_LEN];
}
Call_Private_Data;
static size_t sess_private_data_offset = -1;
static size_t call_private_data_offset = -1;
/* A simple module that collects cookies from the server responses and
includes them in future calls to the server. */
static void
call_issue (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call_Private_Data *cpriv;
Sess_Private_Data *priv;
Sess *sess;
Call *call;
assert (et == EV_CALL_ISSUE && object_is_call (obj));
call = (Call *) obj;
cpriv = CALL_PRIVATE_DATA (call);
if (cpriv->cookie_present)
/* don't do anything if cookie has been set already */
return;
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
if (priv->cookie_len > 0)
{
if (DBG > 1)
fprintf (stderr, "call_issue.%ld: inserting `%s'\n",
call->id, priv->cookie);
cpriv->cookie_present = 1;
memcpy (cpriv->cookie, priv->cookie, priv->cookie_len + 1);
call_append_request_header (call, cpriv->cookie, priv->cookie_len);
}
}
static void
call_recv_hdr (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
char *hdr, *start, *end;
Sess_Private_Data *priv;
size_t len;
struct iovec *line;
Sess *sess;
Call *call;
assert (et == EV_CALL_RECV_HDR && object_is_call (obj));
call = (Call *) obj;
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
line = callarg.vp;
hdr = line->iov_base;
if (tolower (hdr[0]) == 's' && line->iov_len > 12
&& strncasecmp (hdr + 1, "et-cookie: ", 11) == 0)
{
/* munch time! */
start = hdr + 12;
end = strchr (start, ';');
if (!end)
end = hdr + line->iov_len;
len = end - start;
if (DBG > 0 && priv->cookie_len > 0)
fprintf (stderr, "%s: can't handle more than one "
"cookie at a time, replacing existing one\n", prog_name);
if (len + 10 >= MAX_COOKIE_LEN)
{
fprintf (stderr, "%s.sess_cookie: truncating cookie to %d bytes\n",
prog_name, MAX_COOKIE_LEN - 11);
len = MAX_COOKIE_LEN - 11;
}
memcpy (priv->cookie, "Cookie: ", 8);
memcpy (priv->cookie + 8, start, len);
memcpy (priv->cookie + 8 + len, "\r\n", 2);
priv->cookie[10 + len] = '\0';
priv->cookie_len = len + 10;
if (DBG > 0)
fprintf (stderr, "%s: got cookie `%s'\n", prog_name, start);
}
}
static void
init (void)
{
Any_Type arg;
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
call_private_data_offset = object_expand (OBJ_CALL,
sizeof (Call_Private_Data));
arg.l = 0;
event_register_handler (EV_CALL_ISSUE, call_issue, arg);
event_register_handler (EV_CALL_RECV_HDR, call_recv_hdr, arg);
}
Load_Generator sess_cookie =
{
"per-session cookie manager",
init,
no_op,
no_op
};
07070100000022000081ED0000000000000000000000015FCD000200002E10000000000000000000000000000000000000002D00000000httperf-0.9.0+git.20201206/src/gen/session.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* A session consists of a number of calls. Workload generators such
as wsess and wsesslog determine when and how to issue calls. This
module is responsible for the actual mechanics of issuing the
calls. This includes creating and managing connections as
necessary. Connection management can be controlled by command-line
options --max-piped-calls=Np and --max-connections=Nc. This module
creates up to Nc concurrent connections to dispatch calls. On each
connection, up to Np pipelined requests can be issued. When no
more calls can be issued, this module waits until some of the
pending calls complete.
Note that HTTP/1.1 allows a server to close a connection pretty
much any time it feels like. This means that a session may fail
(be closed) while there pipelined calls are pending. In such a
case, this module takes care of creating a new connection and
re-issuing the calls that were pending on the failed connection.
A session is considered to fail if:
(a) any operation exceeds the timeout parameters, or
(b) a connection closes on us before we received at least one
reply, or
(c) param.failure_status is non-zero and the reply status of a call
matches this failure status.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <sess.h>
#include <session.h>
#define MAX_CONN 4 /* max # of connections per session */
#define MAX_PIPED 32 /* max # of calls that can be piped */
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
#define CONN_PRIVATE_DATA(c) \
((Conn_Private_Data *) ((char *)(c) + conn_private_data_offset))
#define CALL_PRIVATE_DATA(c) \
((Call_Private_Data *) ((char *)(c) + call_private_data_offset))
typedef struct Sess_Private_Data
{
struct Conn_Info
{
Conn *conn; /* connection or NULL */
u_int is_connected : 1; /* is connection ready for use? */
u_int is_successful : 1; /* got at least one reply on this conn? */
/* Ring-buffer of pending calls: */
u_int num_pending; /* # of calls pending */
u_int num_sent; /* # of calls sent so far */
u_int rd; /* first pending call */
u_int wr; /* where to insert next call */
Call *call[MAX_PIPED];
}
conn_info[MAX_CONN];
}
Sess_Private_Data;
typedef struct Conn_Private_Data
{
Sess *sess;
struct Conn_Info *ci; /* pointer to relevant conn-info */
}
Conn_Private_Data;
typedef struct Call_Private_Data
{
Sess *sess;
}
Call_Private_Data;
static size_t sess_private_data_offset = -1;
static size_t conn_private_data_offset = -1;
static size_t call_private_data_offset = -1;
static size_t max_qlen;
static void
create_conn (Sess *sess, struct Conn_Info *ci)
{
Conn_Private_Data *cpriv;
/* No connection yet (or anymore). Create a new connection. Note
that CI->CONN is NOT reference-counted. This is again to avoid
introducing recursive dependencies (see also comment regarding
member CONN in call.h). */
ci->conn = conn_new ();
if (!ci->conn)
{
sess_failure (sess);
return;
}
cpriv = CONN_PRIVATE_DATA (ci->conn);
cpriv->sess = sess;
cpriv->ci = ci;
ci->is_connected = 0;
ci->is_successful = 0;
ci->num_sent = 0; /* (re-)send all pending calls */
#ifdef HAVE_SSL
if (param.ssl_reuse && ci->conn->ssl && sess->ssl)
{
if (DBG > 0)
fprintf (stderr, "create_conn: reusing SSL session %p\n",
(void *) sess->ssl);
SSL_copy_session_id (ci->conn->ssl, sess->ssl);
}
#endif
if (core_connect (ci->conn) < 0)
sess_failure (sess);
}
static void
send_calls (Sess *sess, struct Conn_Info *ci)
{
u_int rd;
int i;
if (!ci->conn)
{
create_conn (sess, ci);
return;
}
if (!ci->is_connected)
/* wait until connection is connected (or has failed) */
return;
rd = (ci->rd + ci->num_sent) % MAX_PIPED;
for (i = ci->num_sent; i < ci->num_pending; ++i)
{
core_send (ci->conn, ci->call[rd]);
++ci->num_sent;
rd = (rd + 1) % MAX_PIPED;
}
}
static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
struct Conn_Info *ci;
Sess *sess;
int i, j, rd;
assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
for (i = 0; i < param.max_conns; ++i)
{
ci = priv->conn_info + i;
if (ci->conn)
core_close (ci->conn);
rd = ci->rd;
for (j = 0; j < ci->num_pending; ++j)
{
call_dec_ref (ci->call[rd]);
rd = (rd + 1) % MAX_PIPED;
}
}
}
static void
conn_connected (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Conn_Private_Data *cpriv;
struct Conn_Info *ci;
Sess *sess;
Conn *conn;
assert (et == EV_CONN_CONNECTED && object_is_conn (obj));
conn = (Conn *) obj;
cpriv = CONN_PRIVATE_DATA (conn);
sess = cpriv->sess;
ci = cpriv->ci;
ci->is_connected = 1;
#ifdef HAVE_SSL
if (param.ssl_reuse && !sess->ssl && ci->conn->ssl)
{
sess->ssl = SSL_dup (ci->conn->ssl);
if (DBG > 0)
fprintf (stderr, "create_conn: cached SSL session %p as %p\n",
(void *) ci->conn->ssl, (void *) sess->ssl);
}
#endif /* HAVE_SSL */
send_calls (sess, ci);
}
static void
conn_failed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Conn_Private_Data *cpriv;
struct Conn_Info *ci;
Conn *conn;
Sess *sess;
assert (et == EV_CONN_FAILED && object_is_conn (obj));
conn = (Conn *) obj;
cpriv = CONN_PRIVATE_DATA (conn);
sess = cpriv->sess;
ci = cpriv->ci;
if (ci->is_successful || param.retry_on_failure)
/* try to create a new connection so we can issue the remaining
calls. */
create_conn (sess, ci);
else
/* The connection failed before we got even one reply, so declare
the session as dead... */
sess_failure (cpriv->sess);
}
static void
conn_timeout (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Conn_Private_Data *cpriv;
Conn *conn;
/* doh, this session is dead now... */
assert (et == EV_CONN_TIMEOUT && object_is_conn (obj));
conn = (Conn *) obj;
cpriv = CONN_PRIVATE_DATA (conn);
sess_failure (cpriv->sess);
}
static void
call_done (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Conn_Private_Data *cpriv;
struct Conn_Info *ci;
Sess *sess;
Conn *conn;
Call *call;
assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
call = (Call *) obj;
conn = call->conn;
cpriv = CONN_PRIVATE_DATA (conn);
sess = cpriv->sess;
ci = cpriv->ci;
ci->is_successful = 1; /* conn has received at least one reply */
/* remove the call from the conn_info structure */
assert (ci->call[ci->rd] == call && ci->num_pending > 0 && ci->num_sent > 0);
ci->call[ci->rd] = 0;
ci->rd = (ci->rd + 1) % MAX_PIPED;
--ci->num_pending;
--ci->num_sent;
/* if the reply status matches the failure status, the session has
failed */
if (param.failure_status && call->reply.status == param.failure_status)
{
if (param.retry_on_failure)
session_issue_call (sess, call);
else
sess_failure (sess);
}
call_dec_ref (call);
if (param.http_version < 0x10001)
{
/* Rather than waiting for the connection to close on us, we
close it pro-actively (this is what a pre-1.1 browser would
do. */
core_close (ci->conn);
ci->conn = 0;
}
}
void
session_init (void)
{
Any_Type arg;
if (!param.max_conns)
param.max_conns = MAX_CONN;
if (!param.max_piped)
{
if (param.http_version >= 0x10001)
param.max_piped = MAX_PIPED;
else
/* no pipelining before HTTP/1.1... */
param.max_piped = 1;
}
if (param.max_conns > MAX_CONN)
{
fprintf (stderr, "%s.session_init: --max-conns must be <= %u\n",
prog_name, MAX_CONN);
exit (1);
}
if (param.max_piped > MAX_PIPED)
{
fprintf (stderr, "%s.session_init: --max-piped-calls must be <= %u\n",
prog_name, MAX_PIPED);
exit (1);
}
max_qlen = param.max_conns * param.max_piped;
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
conn_private_data_offset = object_expand (OBJ_CONN,
sizeof (Conn_Private_Data));
call_private_data_offset = object_expand (OBJ_CALL,
sizeof (Call_Private_Data));
arg.l = 0;
event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
event_register_handler (EV_CONN_CONNECTED, conn_connected, arg);
event_register_handler (EV_CONN_FAILED, conn_failed, arg);
event_register_handler (EV_CONN_TIMEOUT, conn_timeout, arg);
event_register_handler (EV_CALL_RECV_STOP, call_done, arg);
}
size_t
session_max_qlen (Sess *sess)
{
return max_qlen;
}
size_t
session_current_qlen (Sess *sess)
{
Sess_Private_Data *priv;
size_t num_pending = 0;
int i;
priv = SESS_PRIVATE_DATA (sess);
for (i = 0; i < param.max_conns; ++i)
num_pending += priv->conn_info[i].num_pending;
return num_pending;
}
int
session_issue_call (Sess *sess, Call *call)
{
Call_Private_Data *cpriv;
Sess_Private_Data *priv;
struct Conn_Info *ci;
int i;
priv = SESS_PRIVATE_DATA (sess);
cpriv = CALL_PRIVATE_DATA (call);
cpriv->sess = sess;
for (i = 0; i < param.max_conns; ++i)
{
ci = priv->conn_info + i;
if (ci->num_pending < param.max_piped)
{
++ci->num_pending;
ci->call[ci->wr] = call;
call_inc_ref (call);
ci->wr = (ci->wr + 1) % MAX_PIPED;
send_calls (sess, ci);
return 0;
}
}
fprintf (stderr, "%s.session_issue_call: too many calls pending!\n"
"\tIncrease --max-connections and/or --max-piped-calls.\n",
prog_name);
exit (1);
}
Sess *
session_get_sess_from_conn (Conn *conn)
{
assert (object_is_conn (conn));
return CONN_PRIVATE_DATA (conn)->sess;
}
Sess *
session_get_sess_from_call (Call *call)
{
assert (object_is_call (call));
return CALL_PRIVATE_DATA (call)->sess;
}
07070100000023000081ED0000000000000000000000015FCD00020000093D000000000000000000000000000000000000002D00000000httperf-0.9.0+git.20201206/src/gen/session.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef session_h
#define session_h
#include <sess.h>
extern void session_init (void); /* initialize session module */
/* Maximum number of calls that can be queued on a session. */
extern size_t session_max_qlen (Sess *sess);
/* Current number of calls that are queued on the session. */
extern size_t session_current_qlen (Sess *sess);
/* Issue call CALL on session SESS. Returns negative number in case
of failure. */
extern int session_issue_call (Sess *sess, Call *call);
/* Given a connection object, find the session object that the
connection belongs to. */
extern Sess *session_get_sess_from_conn (Conn *conn);
/* Given a call object, find the session object that the call
belongs to. */
extern Sess *session_get_sess_from_call (Call *call);
#endif /* session_h */
07070100000024000081ED0000000000000000000000015FCD0002000008D8000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/uri_fixed.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Causes calls to make a request to the fixed URI specified by
PARAM.URI. */
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <call.h>
#include <localevent.h>
static size_t uri_len;
static void
set_uri (Event_Type et, Call *call)
{
assert (et == EV_CALL_NEW && object_is_call (call));
call_set_uri (call, param.uri, uri_len);
}
static void
init (void)
{
Any_Type arg;
uri_len = strlen (param.uri);
arg.l = 0;
event_register_handler (EV_CALL_NEW, (Event_Handler) set_uri, arg);
}
Load_Generator uri_fixed =
{
"fixed url",
init,
no_op,
no_op
};
07070100000025000081ED0000000000000000000000015FCD0002000012FD000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/uri_wlog.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* This load generator can be used to recreate a workload based on a
server log file.
This module can be used in conjunction with two comands to help in
extracting information from the httpd CLF file (contact
eranian@hpl.hp.com).
Please note that you don't necessary need any of those tools. You
can recreate the list of URIs by hand or with any other programs as
long as you respect the format expected by this module (and also
provided than you have the corresponding document tree on the
server side).
The format of the file used by this module is very simple (maybe
too simple):
URI1\0URI2\0......URIn\0
It is a simple concatenated list of URI separated by the \0 (end of
string) marker.
This way, we don't need any parsing of the string when generating
the URI.
You can choose to loop on te list of URIs by using the following
command line option to httperf:
% httperf .... --wlog y,my_uri_file
Otherwise httperf will stop once it reaches the end of the list.
Any comment on this module contact eranian@hpl.hp.com or
davidm@hpl.hp.com. */
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
static char *fbase, *fend, *fcurrent;
static void
set_uri (Event_Type et, Call * c)
{
int len, did_wrap = 0;
const char *uri;
assert (et == EV_CALL_NEW && object_is_call (c));
do
{
if (fcurrent >= fend)
{
if (did_wrap)
panic ("%s: %s does not contain any valid URIs\n",
prog_name, param.wlog.file);
did_wrap = 1;
/* We reached the end of the uri list so wrap around to the
beginning. If not looping, also ask for the test to stop
as soon as possible (the current request will still go
out, but httperf won't wait for its reply to show up). */
fcurrent = fbase;
if (!param.wlog.do_loop)
core_exit ();
}
uri = fcurrent;
len = strlen (fcurrent);
call_set_uri (c, uri, len);
fcurrent += len + 1;
}
while (len == 0);
if (verbose)
printf ("%s: accessing URI `%s'\n", prog_name, uri);
}
void
init_wlog (void)
{
struct stat st;
Any_Type arg;
int fd;
fd = open (param.wlog.file, O_RDONLY, 0);
if (fd == -1)
panic ("%s: can't open %s\n", prog_name, param.wlog.file);
fstat (fd, &st);
if (st.st_size == 0)
panic ("%s: file %s is empty\n", prog_name, param.wlog.file);
/* mmap anywhere in address space: */
fbase = (char *) mmap (0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fbase == (char *) -1)
panic ("%s: can't mmap the file: %s\n", prog_name, strerror (errno));
close (fd);
/* set the upper boundary: */
fend = fbase + st.st_size;
/* set current entry: */
fcurrent = fbase;
arg.l = 0;
event_register_handler (EV_CALL_NEW, (Event_Handler) set_uri, arg);
}
static void
stop_wlog (void)
{
munmap (fbase, fend - fbase);
}
Load_Generator uri_wlog =
{
"Generates URIs based on a predetermined list",
init_wlog,
no_op,
stop_wlog
};
07070100000026000081ED0000000000000000000000015FCD000200000ECC000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/uri_wset.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Causes accesses to a fixed set of files (working set) in such a way
that is likely to cause disk I/O with a certain probability. */
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <call.h>
#include <localevent.h>
#define MAX_URI_LEN 128
#define CALL_PRIVATE_DATA(c) \
((void *) ((char *)(c) + call_private_data_offset))
static double miss_prob;
static unsigned file_num;
static size_t call_private_data_offset;
static size_t uri_prefix_len;
static void
set_uri (Event_Type et, Call *c)
{
char *cp, *buf_end;
unsigned j, n;
assert (et == EV_CALL_NEW && object_is_call (c));
miss_prob += param.wset.target_miss_rate;
if (miss_prob >= 1.0)
{
/* generate (what we hope to be) a miss */
miss_prob -= 1.0;
file_num += param.client.num_clients;
if (file_num >= param.wset.num_files)
file_num -= param.wset.num_files;
}
/* fill in extension: */
buf_end = (char *) CALL_PRIVATE_DATA (c) + MAX_URI_LEN;
cp = buf_end - 6;
memcpy (cp, ".html", 6);
/* fill in file & pathname: */
n = file_num;
for (j = 1; j < param.wset.num_files; j *= 10, n /= 10)
{
cp -= 2;
cp[0] = '/'; cp[1] = '0' + (n % 10);
}
/* fill in the uri prefix specified by param.uri: */
cp -= uri_prefix_len;
if (cp < (char *) CALL_PRIVATE_DATA (c))
{
fprintf (stderr, "%s.uri_wset: URI buffer overflow!\n", prog_name);
exit (1);
}
memcpy (cp, param.uri, uri_prefix_len);
call_set_uri (c, cp, (buf_end - cp) - 1);
if (verbose)
printf ("%s: accessing URI `%s'\n", prog_name, cp);
}
static void
init (void)
{
Any_Type arg;
miss_prob = drand48 ();
file_num = param.client.id;
call_private_data_offset = object_expand (OBJ_CALL, MAX_URI_LEN);
uri_prefix_len = strlen (param.uri);
if (param.uri[uri_prefix_len - 1] == '/')
{
++param.uri;
--uri_prefix_len;
}
arg.l = 0;
event_register_handler (EV_CALL_NEW, (Event_Handler) set_uri, arg);
}
Load_Generator uri_wset =
{
"Generates URIs accessing a working-set at a given rate",
init,
no_op,
no_op
};
07070100000027000081ED0000000000000000000000015FCD0002000016EF000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/gen/wsess.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Creates sessions at the fixed rate PARAM.RATE. */
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <rate.h>
#include <session.h>
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
typedef struct Sess_Private_Data
{
u_int num_calls_in_this_burst; /* # of calls created for this burst */
u_int num_calls_target; /* total # of calls desired */
u_int num_calls_destroyed; /* # of calls destroyed so far */
struct Timer *timer; /* timer for session think time */
}
Sess_Private_Data;
static size_t sess_private_data_offset;
static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;
static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
int i, to_create, retval;
Call *call;
/* Mimic browser behavior of fetching html object, then a couple of
embedded objects: */
to_create = 1;
if (priv->num_calls_in_this_burst > 0)
to_create = param.burst_len - priv->num_calls_in_this_burst;
priv->num_calls_in_this_burst += to_create;
for (i = 0; i < to_create; ++i)
{
call = call_new ();
if (!call)
{
sess_failure (sess);
return;
}
retval = session_issue_call (sess, call);
call_dec_ref (call);
if (retval < 0)
return;
}
}
static void
user_think_time_expired (struct Timer *t, Any_Type arg)
{
Sess *sess = arg.vp;
Sess_Private_Data *priv;
assert (object_is_sess (sess));
priv = SESS_PRIVATE_DATA (sess);
priv->timer = 0;
issue_calls (sess, priv);
}
static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Any_Type arg;
Sess *sess;
Call *call;
Sess_Private_Data *priv;
assert (et == EV_CALL_DESTROYED && object_is_call (obj));
call = (Call *) obj;
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
++priv->num_calls_destroyed;
if (priv->num_calls_destroyed >= param.wsess.num_calls)
{
/* we're done with this session */
if (!sess->failed)
sess_dec_ref (sess);
}
else if (priv->num_calls_in_this_burst < param.burst_len)
/* now that we received the reply to the first call in this burst,
create the remaining calls */
issue_calls (sess, priv);
else if (priv->num_calls_destroyed >= priv->num_calls_target)
{
/* we're done with this burst---schedule the user-think-time timer */
priv->num_calls_in_this_burst = 0;
priv->num_calls_target += param.burst_len;
assert (!priv->timer);
arg.vp = sess;
priv->timer = timer_schedule (user_think_time_expired, arg,
param.wsess.think_time);
}
}
/* Create a new session. */
static int
sess_create (Any_Type arg)
{
Sess_Private_Data *priv;
Sess *sess;
if (num_sessions_generated++ >= param.wsess.num_sessions)
return -1;
sess = sess_new ();
if (!sess)
return 1;
priv = SESS_PRIVATE_DATA (sess);
priv->num_calls_target = param.burst_len;
issue_calls (sess, SESS_PRIVATE_DATA (sess));
return 0;
}
static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
if (priv->timer)
{
timer_cancel (priv->timer);
priv->timer = 0;
}
if (++num_sessions_destroyed >= param.wsess.num_sessions)
core_exit ();
}
static void
init (void)
{
Any_Type arg;
session_init ();
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
rg_sess.rate = ¶m.rate;
rg_sess.tick = sess_create;
rg_sess.arg.l = 0;
arg.l = 0;
event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}
static void
start (void)
{
rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}
Load_Generator wsess =
{
"creates session workload",
init,
start,
no_op
};
07070100000028000081ED0000000000000000000000015FCD00020000437A000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/wsesslog.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Creates sessions at the fixed rate PARAM.RATE. The session descriptions
are read in from a configuration file.
There is currently no tool that translates from standard log
formats to the format accepted by this module.
An example input file follows:
#
# This file specifies the potentially-bursty uri sequence for a number of
# user sessions. The format rules of this file are as follows:
#
# Comment lines start with a '#' as the first character. # anywhere else
# is considered part of the uri.
#
# Lines with only whitespace delimit session definitions (multiple blank
# lines do not generate "null" sessions).
#
# Lines otherwise specify a uri-sequence (1 uri per line). If the
# first character of the line is whitespace (e.g. space or tab), the
# uri is considered to be part of a burst that is sent out after the
# previous non-burst uri.
#
# session 1 definition (this is a comment)
/foo.html
/pict1.gif
/pict2.gif
/foo2.html
/pict3.gif
/pict4.gif
#session 2 definition
/foo3.html
/foo4.html
/pict5.gif
Any comment on this module contact carter@hpl.hp.com. */
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <conn.h>
#include <call.h>
#include <core.h>
#include <localevent.h>
#include <rate.h>
#include <session.h>
/* Maximum number of sessions that can be defined in the configuration
file. */
#define MAX_SESSION_TEMPLATES 1000
#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
typedef struct req REQ;
struct req
{
REQ *next;
int method;
char *uri;
int uri_len;
char *contents;
int contents_len;
char extra_hdrs[50]; /* plenty for "Content-length: 1234567890" */
int extra_hdrs_len;
};
typedef struct burst BURST;
struct burst
{
BURST *next;
int num_reqs;
Time user_think_time;
REQ *req_list;
};
typedef struct Sess_Private_Data Sess_Private_Data;
struct Sess_Private_Data
{
u_int num_calls_in_this_burst; /* # of calls created for this burst */
u_int num_calls_target; /* total # of calls desired */
u_int num_calls_destroyed; /* # of calls destroyed so far */
struct Timer *timer; /* timer for session think time */
int total_num_reqs; /* total number of requests in this session */
BURST *current_burst; /* the current burst we're working on */
REQ *current_req; /* the current request we're working on */
};
/* Methods allowed for a request: */
enum
{
HM_DELETE, HM_GET, HM_HEAD, HM_OPTIONS, HM_POST, HM_PUT, HM_PROPFIND, HM_REPORT, HM_TRACE,
HM_LEN
};
static const char *call_method_name[] =
{
"DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "PROPFIND", "REPORT", "TRACE"
};
static size_t sess_private_data_offset;
static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;
/* This is an array rather than a list because we may want different
httperf clients to start at different places in the sequence of
sessions. */
static int num_templates;
static int next_session_template;
static Sess_Private_Data session_templates[MAX_SESSION_TEMPLATES] =
{
{ 0, }
};
static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
if (priv->timer)
{
timer_cancel (priv->timer);
priv->timer = 0;
}
if (++num_sessions_destroyed >= param.wsesslog.num_sessions)
core_exit ();
}
static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
int i, to_create, retval, n;
const char *method_str;
Call *call;
REQ *req;
/* Mimic browser behavior of fetching html object, then a couple of
embedded objects: */
to_create = 1;
if (priv->num_calls_in_this_burst > 0)
to_create = priv->current_burst->num_reqs - priv->num_calls_in_this_burst;
n = session_max_qlen (sess) - session_current_qlen (sess);
if (n < to_create)
to_create = n;
priv->num_calls_in_this_burst += to_create;
for (i = 0; i < to_create; ++i)
{
call = call_new ();
if (!call)
{
sess_failure (sess);
return;
}
/* fill in the new call: */
req = priv->current_req;
if (req == NULL)
panic ("%s: internal error, requests ran past end of burst\n",
prog_name);
method_str = call_method_name[req->method];
call_set_method (call, method_str, strlen (method_str));
call_set_uri (call, req->uri, req->uri_len);
if (req->contents_len > 0)
{
/* add "Content-length:" header and contents, if necessary: */
call_append_request_header (call, req->extra_hdrs,
req->extra_hdrs_len);
call_set_contents (call, req->contents, req->contents_len);
}
priv->current_req = req->next;
if (DBG > 0)
fprintf (stderr, "%s: accessing URI `%s'\n", prog_name, req->uri);
retval = session_issue_call (sess, call);
call_dec_ref (call);
if (retval < 0)
return;
}
}
static void
user_think_time_expired (struct Timer *t, Any_Type arg)
{
Sess *sess = arg.vp;
Sess_Private_Data *priv;
assert (object_is_sess (sess));
priv = SESS_PRIVATE_DATA (sess);
priv->timer = 0;
issue_calls (sess, priv);
}
/* Create a new session and fill in our private information. */
static int
sess_create (Any_Type arg)
{
Sess_Private_Data *priv, *template;
Sess *sess;
if (num_sessions_generated++ >= param.wsesslog.num_sessions)
return -1;
sess = sess_new ();
template = &session_templates[next_session_template];
if (++next_session_template >= num_templates)
next_session_template = 0;
priv = SESS_PRIVATE_DATA (sess);
priv->current_burst = template->current_burst;
priv->current_req = priv->current_burst->req_list;
priv->total_num_reqs = template->total_num_reqs;
priv->num_calls_target = priv->current_burst->num_reqs;
if (DBG > 0)
fprintf (stderr, "Starting session, first burst_len = %d\n",
priv->num_calls_target);
issue_calls (sess, SESS_PRIVATE_DATA (sess));
return 0;
}
static void
prepare_for_next_burst (Sess *sess, Sess_Private_Data *priv)
{
Time think_time;
Any_Type arg;
if (priv->current_burst != NULL)
{
think_time = priv->current_burst->user_think_time;
/* advance to next burst: */
priv->current_burst = priv->current_burst->next;
if (priv->current_burst != NULL)
{
priv->current_req = priv->current_burst->req_list;
priv->num_calls_in_this_burst = 0;
priv->num_calls_target += priv->current_burst->num_reqs;
assert (!priv->timer);
arg.vp = sess;
priv->timer = timer_schedule (user_think_time_expired,
arg, think_time);
}
}
}
static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
Call *call;
assert (et == EV_CALL_DESTROYED && object_is_call (obj));
call = (Call *) obj;
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
if (sess->failed)
return;
++priv->num_calls_destroyed;
if (priv->num_calls_destroyed >= priv->total_num_reqs)
/* we're done with this session */
sess_dec_ref (sess);
else if (priv->num_calls_in_this_burst < priv->current_burst->num_reqs)
issue_calls (sess, priv);
else if (priv->num_calls_destroyed >= priv->num_calls_target)
prepare_for_next_burst (sess, priv);
}
/* Allocates memory for a REQ and assigns values to data members.
This is used during configuration file parsing only. */
static REQ*
new_request (char *uristr)
{
REQ *retptr;
retptr = (REQ *) malloc (sizeof (*retptr));
if (retptr == NULL || uristr == NULL)
panic ("%s: ran out of memory while parsing %s\n",
prog_name, param.wsesslog.file);
memset (retptr, 0, sizeof (*retptr));
retptr->uri = uristr;
retptr->uri_len = strlen (uristr);
retptr->method = HM_GET;
return retptr;
}
/* Like new_request except this is for burst descriptors. */
static BURST*
new_burst (REQ *r)
{
BURST *retptr;
retptr = (BURST *) malloc (sizeof (*retptr));
if (retptr == NULL)
panic ("%s: ran out of memory while parsing %s\n",
prog_name, param.wsesslog.file);
memset (retptr, 0, sizeof (*retptr));
retptr->user_think_time = param.wsesslog.think_time;
retptr->req_list = r;
return retptr;
}
/* Read in session-defining configuration file and create in-memory
data structures from which to assign uri_s to calls. */
static void
parse_config (void)
{
FILE *fp;
int lineno, i, reqnum;
Sess_Private_Data *sptr;
char line[500000]; /* some uri's get pretty long */
char uri[500000]; /* some uri's get pretty long */
char method_str[1000];
char this_arg[500000];
char contents[500000];
double think_time;
int bytes_read;
REQ *reqptr;
BURST *bptr, *current_burst = 0;
char *from, *to, *parsed_so_far;
int ch;
int single_quoted, double_quoted, escaped, done;
fp = fopen (param.wsesslog.file, "r");
if (fp == NULL)
panic ("%s: can't open %s\n", prog_name, param.wsesslog.file);
num_templates = 0;
sptr = &session_templates[0];
for (lineno = 1; fgets (line, sizeof (line), fp); lineno++)
{
if (line[0] == '#')
continue; /* skip over comment lines */
if (sscanf (line,"%s%n", uri, &bytes_read) != 1)
{
/* must be a session-delimiting blank line */
if (sptr->current_req != NULL)
sptr++; /* advance to next session */
continue;
}
/* looks like a request-specifying line */
reqptr = new_request (strdup (uri));
if (sptr->current_req == NULL)
{
num_templates++;
if (num_templates > MAX_SESSION_TEMPLATES)
panic ("%s: too many sessions (%d) specified in %s\n",
prog_name, num_templates, param.wsesslog.file);
current_burst = sptr->current_burst = new_burst (reqptr);
}
else
{
if (!isspace (line[0]))
/* this uri starts a new burst */
current_burst = (current_burst->next = new_burst (reqptr));
else
sptr->current_req->next = reqptr;
}
/* do some common steps for all new requests */
current_burst->num_reqs++;
sptr->total_num_reqs++;
sptr->current_req = reqptr;
/* parse rest of line to specify additional parameters of this
request and burst */
parsed_so_far = line + bytes_read;
while (sscanf (parsed_so_far, " %s%n", this_arg, &bytes_read) == 1)
{
if (sscanf (this_arg, "method=%s", method_str) == 1)
{
for (i = 0; i < HM_LEN; i++)
{
if (!strncmp (method_str,call_method_name[i],
strlen (call_method_name[i])))
{
sptr->current_req->method = i;
break;
}
}
if (i == HM_LEN)
panic ("%s: did not recognize method '%s' in %s\n",
prog_name, method_str, param.wsesslog.file);
}
else if (sscanf (this_arg, "think=%lf", &think_time) == 1)
current_burst->user_think_time = think_time;
else if (sscanf (this_arg, "contents=%s", contents) == 1)
{
/* this is tricky since contents might be a quoted
string with embedded spaces or escaped quotes. We
should parse this carefully from parsed_so_far */
from = strchr (parsed_so_far, '=') + 1;
to = contents;
single_quoted = FALSE;
double_quoted = FALSE;
escaped = FALSE;
done = FALSE;
while ((ch = *from++) != '\0' && !done)
{
if (escaped == TRUE)
{
switch (ch)
{
case 'n':
*to++ = '\n';
break;
case 'r':
*to++ = '\r';
break;
case 't':
*to++ = '\t';
break;
case '\n':
*to++ = '\n';
/* this allows an escaped newline to
continue the parsing to the next line. */
if (fgets(line,sizeof(line),fp) == NULL)
{
lineno++;
panic ("%s: premature EOF seen in '%s'\n",
prog_name, param.wsesslog.file);
}
parsed_so_far = from = line;
break;
default:
*to++ = ch;
break;
}
escaped = FALSE;
}
else if (ch == '"' && double_quoted)
{
double_quoted = FALSE;
}
else if (ch == '\'' && single_quoted)
{
single_quoted = FALSE;
}
else
{
switch (ch)
{
case '\t':
case '\n':
case ' ':
if (single_quoted == FALSE &&
double_quoted == FALSE)
done = TRUE; /* we are done */
else
*to++ = ch;
break;
case '\\': /* backslash */
escaped = TRUE;
break;
case '"': /* double quote */
if (single_quoted)
*to++ = ch;
else
double_quoted = TRUE;
break;
case '\'': /* single quote */
if (double_quoted)
*to++ = ch;
else
single_quoted = TRUE;
break;
default:
*to++ = ch;
break;
}
}
}
*to = '\0';
from--; /* back up 'from' to '\0' or white-space */
bytes_read = from - parsed_so_far;
if ((sptr->current_req->contents_len = strlen (contents)) != 0)
{
sptr->current_req->contents = strdup (contents);
snprintf (sptr->current_req->extra_hdrs,
sizeof(sptr->current_req->extra_hdrs),
"Content-length: %d\r\n",
sptr->current_req->contents_len);
sptr->current_req->extra_hdrs_len =
strlen (sptr->current_req->extra_hdrs);
}
}
else
{
/* do not recognize this arg */
panic ("%s: did not recognize arg '%s' in %s\n",
prog_name, this_arg, param.wsesslog.file);
}
parsed_so_far += bytes_read;
}
}
fclose (fp);
if (DBG > 3)
{
fprintf (stderr,"%s: session list follows:\n\n", prog_name);
for (i = 0; i < num_templates; i++)
{
sptr = &session_templates[i];
fprintf (stderr, "#session %d (total_reqs=%d):\n",
i, sptr->total_num_reqs);
for (bptr = sptr->current_burst; bptr; bptr = bptr->next)
{
for (reqptr = bptr->req_list, reqnum = 0;
reqptr;
reqptr = reqptr->next, reqnum++)
{
if (reqnum >= bptr->num_reqs)
panic ("%s: internal error detected in parsing %s\n",
prog_name, param.wsesslog.file);
if (reqnum > 0)
fprintf (stderr, "\t");
fprintf (stderr, "%s", reqptr->uri);
if (reqnum == 0
&& bptr->user_think_time != param.wsesslog.think_time)
fprintf (stderr, " think=%0.2f",
(double) bptr->user_think_time);
if (reqptr->method != HM_GET)
fprintf (stderr," method=%s",
call_method_name[reqptr->method]);
if (reqptr->contents != NULL)
fprintf (stderr, " contents='%s'", reqptr->contents);
fprintf (stderr, "\n");
}
}
fprintf (stderr, "\n");
}
}
}
static void
init (void)
{
Any_Type arg;
parse_config ();
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
rg_sess.rate = ¶m.rate;
rg_sess.tick = sess_create;
rg_sess.arg.l = 0;
arg.l = 0;
event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
/* This must come last so the session event handlers are executed
before this module's handlers. */
session_init ();
}
static void
start (void)
{
rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}
Load_Generator wsesslog =
{
"creates log-based session workload",
init,
start,
no_op
};
07070100000029000081ED0000000000000000000000015FCD00020000344B000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/wsesspage.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Similar to wsess but instead of generating fixed bursts, each
fetched html page is parsed and the embedded objects are fetched in
a burst.
This is NOT a high performance workload generator! Use it only for
non-performance critical tests. */
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <rate.h>
#include <session.h>
#define CALL_PRIVATE_DATA(c) \
((Call_Private_Data *) ((char *)(c) + call_private_data_offset))
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
typedef struct Call_Private_Data
{
enum
{
P_INITIAL,
P_HTML,
P_CMD, /* we saw a `<' and are scanning for the end of CMD */
P_DASH_ONE, /* looking for the first dash of a comment close */
P_DASH_TWO, /* looking for the second dash of a comment close */
P_RANGLE, /* looking for '>' */
P_SRC, /* we're looking for "src" */
P_DATA, /* we're looking for "data" */
P_LQUOTE, /* we're looking for the left quote of a URI */
P_NAKED_URI, /* we're looking for an unquoted URI */
P_QUOTED_URI /* we're looking for a quoted URI */
}
state;
int buf_len;
char buf[1024];
void *to_free; /* call queue element to free when done */
}
Call_Private_Data;
typedef struct Sess_Private_Data
{
u_int num_created; /* # of calls created in this burst */
u_int num_destroyed; /* # of calls destroyed in this burst */
u_int num_reqs_completed; /* # of user reqs completed */
struct Timer *timer; /* timer for session think time */
struct uri_list
{
struct uri_list *next;
size_t uri_len;
char uri[1]; /* really URI_LEN+1 bytes... */
}
*uri_list;
}
Sess_Private_Data;
static size_t sess_private_data_offset;
static size_t call_private_data_offset;
static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;
static size_t prefix_len;
static char *prefix;
static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
int i, to_create, retval, embedded = 0;
Call_Private_Data *cpriv;
struct uri_list *el;
Call *call;
/* Mimic browser behavior of fetching html object, then a couple of
embedded objects: */
to_create = 1;
if (priv->num_created > 0)
{
to_create = session_max_qlen (sess) - session_current_qlen (sess);
embedded = 1;
}
for (i = 0; i < to_create && (!embedded || priv->uri_list); ++i)
{
++priv->num_created;
call = call_new ();
if (!call)
{
sess_failure (sess);
return;
}
if (embedded)
{
el = priv->uri_list;
priv->uri_list = el->next;
cpriv = CALL_PRIVATE_DATA (call);
cpriv->to_free = el;
call_set_uri (call, el->uri, el->uri_len);
}
if (verbose > 1)
printf ("%s: fetching `%s'\n",
prog_name, (char *)call->req.iov[IE_URI].iov_base);
retval = session_issue_call (sess, call);
call_dec_ref (call);
if (retval < 0)
return;
}
}
static void
fetch_uri (Sess *sess, Sess_Private_Data *priv, Call_Private_Data *cpriv,
const char *uri, size_t uri_len)
{
struct uri_list *el;
int is_relative;
size_t len;
char *dst;
if (strchr (uri, ':'))
{
len = strlen (param.server);
if (strncmp (uri, "http://", 7) == 0
&& strncmp (uri + 7, param.server, len) == 0
&& uri[7 + len] == '/')
{
uri += 7 + len;
uri_len -= 7 + len;
}
else
{
/* Eventually, we may want to create new sessions on the fly,
but for now, we simply punt on non-absolute URIs */
if (verbose > 1)
fprintf (stderr, "%s: ignoring absolute URI `%s'\n",
prog_name, uri);
return;
}
}
is_relative = (uri[0] != '/');
/* enqueue the new uri: */
len = uri_len;
if (is_relative)
len += prefix_len;
el = malloc (sizeof (*el) + len);
if (!el)
panic ("%s.fetch_uri: out of memory!\n", prog_name);
el->uri_len = len;
dst = el->uri;
if (is_relative)
{
memcpy (el->uri, prefix, prefix_len);
dst += prefix_len;
}
memcpy (dst, uri, uri_len + 1);
el->next = priv->uri_list;
priv->uri_list = el;
issue_calls (sess, priv);
}
static void
user_think_time_expired (struct Timer *t, Any_Type arg)
{
Sess *sess = arg.vp;
Sess_Private_Data *priv;
assert (object_is_sess (sess));
priv = SESS_PRIVATE_DATA (sess);
priv->timer = 0;
issue_calls (sess, priv);
}
static void
call_recv_hdr (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call_Private_Data *cpriv;
Sess_Private_Data *priv;
struct iovec *line;
Call *call;
Sess *sess;
char *hdr;
assert (et == EV_CALL_RECV_HDR && object_is_call (obj));
call = (Call *) obj;
cpriv = CALL_PRIVATE_DATA (call);
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
line = callarg.vp;
hdr = line->iov_base;
switch (tolower (hdr[0]))
{
case 'c':
if (line->iov_len >= 23
&& strncasecmp (hdr + 1, "ontent-type: text/html", 22) == 0)
cpriv->state = P_HTML;
break;
case 'l':
if (line->iov_len > 10
&& strncasecmp (hdr + 1, "ocation: ", 9) == 0)
fetch_uri (sess, priv, cpriv, hdr + 10, line->iov_len - 10);
break;
}
}
static void
call_recv_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call_Private_Data *cpriv;
Sess_Private_Data *priv;
const char *cp, *end;
struct iovec *line;
Sess *sess;
Call *call;
int ch;
assert (et == EV_CALL_RECV_DATA && object_is_call (obj));
call = (Call *) obj;
cpriv = CALL_PRIVATE_DATA (call);
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
if (cpriv->state == P_INITIAL)
return; /* not an html object */
line = callarg.vp;
cp = line->iov_base;
end = cp + line->iov_len;
while (cp < end)
{
ch = *cp++;
switch (cpriv->state)
{
case P_INITIAL:
break;
case P_HTML:
cpriv->buf_len = 0;
if (ch == '<')
cpriv->state = P_CMD;
break;
case P_CMD:
if (isspace (ch) || ch == '=')
{
if (cpriv->buf_len > 0)
{
if (DBG > 3)
fprintf (stderr, "found command `%.*s'\n",
cpriv->buf_len, cpriv->buf);
if (cpriv->buf_len == 3
&& strcmp (cpriv->buf, "!--") == 0)
cpriv->state = P_DASH_ONE;
else if (cpriv->buf_len == 5
&& strncasecmp (cpriv->buf, "frame", 5) == 0)
cpriv->state = P_SRC;
else if (cpriv->buf_len == 6
&& strncasecmp (cpriv->buf, "iframe", 6) == 0)
cpriv->state = P_SRC;
else if (cpriv->buf_len == 6
&& strncasecmp (cpriv->buf, "data", 6) == 0)
cpriv->state = P_DATA;
else if (cpriv->buf_len == 3
&& strncasecmp (cpriv->buf, "img", 3) == 0)
cpriv->state = P_SRC;
cpriv->buf_len = 0;
}
else
cpriv->state = P_HTML;
}
else if (ch == '>')
cpriv->state = P_HTML;
else if (cpriv->buf_len < sizeof (cpriv->buf))
cpriv->buf[cpriv->buf_len++] = ch;
break;
case P_DASH_ONE:
if (ch == '-')
cpriv->state = P_DASH_TWO;
break;
case P_DASH_TWO:
cpriv->state = (ch == '-') ? P_RANGLE : P_DASH_ONE;
break;
case P_RANGLE:
if (ch == '>')
cpriv->state = P_HTML;
break;
case P_SRC:
if (ch == '>')
cpriv->state = P_HTML;
else
{
cpriv->buf[cpriv->buf_len++] = ch;
if (cpriv->buf_len == 4)
{
if (strncasecmp (cpriv->buf, "src=", 4) == 0)
{
cpriv->state = P_LQUOTE;
cpriv->buf_len = 0;
}
else
{
memcpy (cpriv->buf, cpriv->buf + 1, 3);
cpriv->buf_len = 3;
}
}
}
break;
case P_DATA:
if (ch == '>')
cpriv->state = P_HTML;
else
{
cpriv->buf[cpriv->buf_len++] = ch;
if (cpriv->buf_len == 5)
{
if (strncasecmp (cpriv->buf, "data=", 5) == 0)
{
cpriv->state = P_LQUOTE;
cpriv->buf_len = 0;
}
else
{
memcpy (cpriv->buf, cpriv->buf + 1, 4);
cpriv->buf_len = 4;
}
}
}
break;
case P_LQUOTE:
if (ch == '"')
cpriv->state = P_QUOTED_URI;
else if (!isspace (ch))
{
cpriv->state = P_NAKED_URI;
cpriv->buf[cpriv->buf_len++] = ch;
}
break;
case P_NAKED_URI:
case P_QUOTED_URI:
if ((cpriv->state == P_QUOTED_URI && ch == '"')
|| (cpriv->state == P_NAKED_URI && isspace (ch)))
{
cpriv->buf[cpriv->buf_len] = '\0';
fetch_uri (sess, priv, cpriv, cpriv->buf, cpriv->buf_len);
cpriv->state = P_HTML;
cpriv->buf_len = 0;
}
else if (cpriv->buf_len < sizeof (cpriv->buf) - 1)
cpriv->buf[cpriv->buf_len++] = ch;
break;
}
}
}
static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call_Private_Data *cpriv;
Sess_Private_Data *priv;
Any_Type arg;
Sess *sess;
Call *call;
assert (et == EV_CALL_DESTROYED && object_is_call (obj));
call = (Call *) obj;
cpriv = CALL_PRIVATE_DATA (call);
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
if (cpriv->to_free)
{
free (cpriv->to_free);
cpriv->to_free = 0;
}
++priv->num_destroyed;
if (sess->failed)
return;
if (priv->uri_list)
/* there are some queued URI's which we may be able to issue now */
issue_calls (sess, priv);
else if (priv->num_destroyed >= priv->num_created)
{
/* we're done with this burst */
if (++priv->num_reqs_completed >= param.wsesspage.num_reqs)
/* we're done with this session */
sess_dec_ref (sess);
else
{
/* schedule the user-think-time timer */
priv->num_created = 0;
assert (!priv->timer);
arg.vp = sess;
priv->timer = timer_schedule (user_think_time_expired, arg,
param.wsesspage.think_time);
}
}
}
/* Create a new session. */
static int
sess_create (Any_Type arg)
{
Sess_Private_Data *priv;
Sess *sess;
if (num_sessions_generated++ >= param.wsesspage.num_sessions)
return -1;
sess = sess_new ();
if (!sess)
return 1;
priv = SESS_PRIVATE_DATA (sess);
issue_calls (sess, SESS_PRIVATE_DATA (sess));
return 0;
}
static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
if (priv->timer)
{
timer_cancel (priv->timer);
priv->timer = 0;
}
if (++num_sessions_destroyed >= param.wsesspage.num_sessions)
core_exit ();
}
static void
init (void)
{
const char *slash;
Any_Type arg;
slash = strrchr (param.uri, '/');
if (slash)
prefix_len = (slash + 1) - param.uri;
else
panic ("%s: URI specified with --uri must be absolute", prog_name);
prefix = strdup (param.uri);
prefix[prefix_len] = '\0';
session_init ();
call_private_data_offset = object_expand (OBJ_CALL,
sizeof (Call_Private_Data));
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
rg_sess.rate = ¶m.rate;
rg_sess.tick = sess_create;
rg_sess.arg.l = 0;
arg.l = 0;
event_register_handler (EV_CALL_RECV_HDR, call_recv_hdr, arg);
event_register_handler (EV_CALL_RECV_DATA, call_recv_data, arg);
event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}
static void
start (void)
{
rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}
Load_Generator wsesspage =
{
"creates sessions that fetch html pages and embedded objects",
init,
start,
no_op
};
0707010000002A000081ED0000000000000000000000015FCD0002000027D2000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/http.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <httperf.h>
#include <http.h>
/* Read a CRLF terminated line of characters into c->reply.line.
Returns 1 when the line is complete, 0 when the line is incomplete
and more data is needed. */
static int
get_line (Call *c, char **bufp, size_t *buf_lenp)
{
size_t to_copy, buf_len = *buf_lenp;
Conn *s = c->conn;
char *buf = *bufp;
const char *eol;
int has_lf;
if (buf_len <= 0)
return 0;
/* Note that core.c guarantees that BUF is '\0' terminated. */
eol = strchr (buf, '\n');
if (eol)
++eol;
else
eol = buf + buf_len;
to_copy = eol - buf;
buf_len -= to_copy;
if (s->line.iov_len + to_copy >= sizeof (s->line_buf))
{
fprintf (stderr,
"%s.get_line: truncating header from %lu to %lu bytes\n",
prog_name, (u_long) (s->line.iov_len + to_copy),
(u_long) sizeof (s->line_buf));
to_copy = sizeof (s->line_buf) - 1 - s->line.iov_len;
}
memcpy ((char *) s->line.iov_base + s->line.iov_len, buf, to_copy);
s->line.iov_len += to_copy;
has_lf = ((char *) s->line.iov_base)[s->line.iov_len - 1] == '\n';
*bufp = (char *) eol;
*buf_lenp = buf_len;
if (has_lf || s->line.iov_len == sizeof (s->line_buf) - 1)
{
/* We got a full header line. Chop off \r\n at the tail if
necessary. */
if (((char *) s->line.iov_base)[s->line.iov_len - 1] == '\n')
{
--s->line.iov_len;
if (((char *) s->line.iov_base)[s->line.iov_len - 1] == '\r')
--s->line.iov_len;
}
((char *) s->line.iov_base)[s->line.iov_len] = '\0';
return 1;
}
return 0;
}
static void
parse_status_line (Call *c, char **bufp, size_t *buf_lenp)
{
char *buf, *buf_start = *bufp;
u_int major, minor, status;
Conn *s = c->conn;
Any_Type arg;
s->is_chunked = 0;
/* default to "infinite" content length: */
s->content_length = ~(size_t) 0;
if (!get_line (c, bufp, buf_lenp))
return;
buf = c->conn->line.iov_base;
if (sscanf (buf, "HTTP/%u.%u %u ", &major, &minor, &status) == 3)
{
c->reply.version = 0x10000*major + minor;
c->reply.status = status;
}
else
{
c->reply.version = 0x10000; /* default to 1.0 */
c->reply.status = 599;
if (c->reply.status == 599)
fprintf (stderr, "%s.parse_status_line: invalid status line `%s'!!\n",
prog_name, buf);
}
if (DBG > 0)
fprintf (stderr,
"parse_status_line.%lu: reply is HTTP/%u.%u, status = %d\n",
c->id, c->reply.version / 0x10000, c->reply.version & 0xffff,
c->reply.status);
/* Determine whether we should be expecting a message body. HEAD
never includes an entity. For other methods, things depend on
the status code. */
if (strcmp ((char *) c->req.iov[IE_METHOD].iov_base, "HEAD") == 0)
s->has_body = 0;
else
{
s->has_body = 1;
switch (status / 100)
{
case 1: /* informational */
s->has_body = 0;
if (status == 100)
{
arg.l = c->reply.status;
event_signal (EV_CALL_RECV_START, (Object *) c, arg);
s->state = S_REPLY_CONTINUE;
goto done;
}
break;
case 2: /* success */
case 3: /* redirection */
switch (status)
{
case 204: /* No Content */
case 205: /* Reset Content */
case 304: /* Not Modified */
s->has_body = 0;
break;
}
break;
case 4: /* client errors */
case 5: /* server errors */
break;
default:
fprintf (stderr, "%s.parse_status_line: bad status %u\n",
prog_name, status);
break;
}
}
arg.l = c->reply.status;
event_signal (EV_CALL_RECV_START, (Object *) c, arg);
if (s->state >= S_CLOSING)
return;
s->state = S_REPLY_HEADER;
done:
c->reply.header_bytes += *bufp - buf_start;
s->line.iov_len = 0;
}
static void
parse_headers (Call *c, char **bufp, size_t *buf_lenp)
{
char *hdr, *buf_start = *bufp;
Conn *s = c->conn;
size_t hdr_len;
Any_Type arg;
while (get_line (c, bufp, buf_lenp) > 0)
{
hdr = s->line.iov_base;
hdr_len = s->line.iov_len;
if (!hdr_len)
{
/* empty header implies end of headers */
if (s->has_body)
if (s->is_chunked)
{
s->content_length = 0;
s->state = S_REPLY_CHUNKED;
}
else
s->state = S_REPLY_DATA;
else if (s->state == S_REPLY_CONTINUE)
s->state = S_REPLY_HEADER;
else
s->state = S_REPLY_DONE;
break;
}
/* process line as a regular header: */
switch (tolower (*hdr))
{
case 'c':
if (strncasecmp (hdr, "content-length:", 15) == 0)
{
hdr += 15;
s->content_length = strtoul (hdr, 0, 10);
if (!s->content_length)
s->has_body = 0;
}
break;
case 't':
if (strncasecmp (hdr, "transfer-encoding:", 18) == 0)
{
hdr += 18;
while (isspace (*hdr))
++hdr;
if (strcasecmp (hdr, "chunked") == 0)
s->is_chunked = 1;
else
fprintf (stderr, "%s.parse_headers: unknown transfer "
"encoding `%s'\n", prog_name, hdr);
}
break;
}
arg.vp = &s->line;
event_signal (EV_CALL_RECV_HDR, (Object *) c, arg);
if (s->state >= S_CLOSING)
return;
s->line.iov_len = 0;
}
c->reply.header_bytes += *bufp - buf_start;
}
static void
parse_footers (Call *c, char **bufp, size_t *buf_lenp)
{
char *hdr, *buf_start = *bufp;
Conn *s = c->conn;
size_t hdr_len;
Any_Type arg;
while (get_line (c, bufp, buf_lenp) > 0)
{
hdr = s->line.iov_base;
hdr_len = s->line.iov_len;
if (!hdr_len)
{
/* empty footer implies end of footers */
s->state = S_REPLY_DONE;
break;
}
/* process line as a regular footer: */
arg.vp = &s->line;
event_signal (EV_CALL_RECV_FOOTER, (Object *) c, arg);
if (s->state >= S_CLOSING)
return;
s->line.iov_len = 0;
}
c->reply.footer_bytes += *bufp - buf_start;
}
static int
parse_data (Call *c, char **bufp, size_t *buf_lenp)
{
size_t bytes_needed, buf_len = *buf_lenp;
Conn *s = c->conn;
char *buf = *bufp;
struct iovec iov;
Any_Type arg;
bytes_needed = (s->content_length - c->reply.content_bytes);
if (buf_len > bytes_needed)
buf_len = bytes_needed;
iov.iov_base = (caddr_t) buf;
iov.iov_len = buf_len;
arg.vp = &iov;
event_signal (EV_CALL_RECV_DATA, (Object *) c, arg);
c->reply.content_bytes += buf_len;
*bufp = buf + buf_len;
*buf_lenp -= buf_len;
return (buf_len == bytes_needed);
}
static void
xfer_chunked (Call *c, char **bufp, size_t *buf_lenp)
{
Conn *s = c->conn;
size_t chunk_length;
char *end;
while (*buf_lenp > 0 && s->state < S_CLOSING)
{
if (c->reply.content_bytes >= s->content_length)
{
/* need to parse next chunk length line: */
if (!get_line (c, bufp, buf_lenp))
return; /* need more data */
if (s->line.iov_len == 0)
continue; /* skip over empty line */
errno = 0;
chunk_length = strtoul (s->line.iov_base, &end, 16);
s->line.iov_len = 0;
if (errno == ERANGE || end == s->line.iov_base)
{
fprintf (stderr, "%s.xfer_chunked: bad chunk line `%s'\n",
prog_name, (char *) s->line.iov_base);
continue;
}
if (chunk_length == 0)
{
/* a final chunk of zero bytes indicates the end of the reply */
s->state = S_REPLY_FOOTER;
return;
}
s->content_length += chunk_length;
}
parse_data (c, bufp, buf_lenp);
}
}
void
http_process_reply_bytes (Call *c, char **bufp, size_t *buf_lenp)
{
Conn *s = c->conn;
struct iovec iov;
Any_Type arg;
iov.iov_base = *bufp;
iov.iov_len = *buf_lenp;
arg.vp = &iov;
event_signal (EV_CALL_RECV_RAW_DATA, (Object *) c, arg);
do
{
switch (s->state)
{
case S_REPLY_STATUS:
parse_status_line (c, bufp, buf_lenp);
break;
case S_REPLY_HEADER:
parse_headers (c, bufp, buf_lenp);
break;
case S_REPLY_FOOTER:
parse_footers (c, bufp, buf_lenp);
break;
case S_REPLY_DATA:
if (parse_data (c, bufp, buf_lenp) && s->state < S_CLOSING)
s->state = S_REPLY_DONE;
break;
case S_REPLY_CONTINUE:
parse_headers (c, bufp, buf_lenp);
break;
case S_REPLY_CHUNKED:
xfer_chunked (c, bufp, buf_lenp);
break;
case S_REPLY_DONE:
return;
default:
fprintf (stderr, "%s.http_process_reply_bytes: bad state %d\n",
prog_name, s->state);
exit (1);
}
}
while (*buf_lenp > 0 && s->state < S_CLOSING);
}
0707010000002B000081ED0000000000000000000000015FCD0002000006A1000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/http.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef http_h
#define http_h
extern void http_process_reply_bytes (Call *c, char **buf, size_t *buf_len);
#endif /* http_h */
0707010000002C000081ED0000000000000000000000015FCD00020000A6F5000000000000000000000000000000000000002900000000httperf-0.9.0+git.20201206/src/httperf.c/*
* Copyright (C) 2000-2007 Hewlett-Packard Company
* Copyright (C) 2007 Ted Bullock <tbullock@comlore.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* Fundamentals:
*
* There are three subsystems to httperf:
*
* 1) The load generator which determines what URI is fetched next.
*
* 2) The core engine that handles the mechanics of issuing a request.
*
* 3) The instrumentation infrastructure that measures various aspects of the
* transaction(s).
*
* Since there is considerable potential variation in all three, it seems like
* an event-based approach might be ideal in tying the three together.
* Ideally, it should be possible to write a new load generator without
* modifications to the other subsystems. Similarly, it should be possible to
* add instrumentation without requiring changes to the load generator or http
* engine.
*
* Axioms: - The only point at which the client will fall back is if the
* client itself is overloaded. There is no point trying to fix up this
* case---simply declare defeat and abort the test.
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __FreeBSD__
/* Required for fpsetmask() under FreeBSD */
#include <ieeefp.h>
#endif
#include <sys/time.h>
#include <generic_types.h>
#include <sys/resource.h> /* after sys/types.h for BSD (in generic_types.h) */
#include <object.h>
#include <timer.h>
#include <conn.h>
#include <call.h>
#include <core.h>
#include <localevent.h>
#include <httperf.h>
#ifdef HAVE_SSL
# include <openssl/rand.h>
#endif
#define RATE_INTERVAL 5.0
const char *prog_name;
int verbose;
int periodic_stats;
Cmdline_Params param;
Time test_time_start;
Time test_time_stop;
struct rusage test_rusage_start;
struct rusage test_rusage_stop;
size_t object_type_size[OBJ_NUM_TYPES];
#ifdef HAVE_SSL
SSL_CTX *ssl_ctx;
#endif
#ifdef DEBUG
int debug_level;
#endif
static Time perf_sample_start;
static struct option longopts[] = {
{"add-header", required_argument, (int *) ¶m.additional_header, 0},
{"add-header-file", required_argument, (int *) ¶m.additional_header_file, 0 },
{"burst-length", required_argument, (int *) ¶m.burst_len, 0},
{"client", required_argument, (int *) ¶m.client, 0},
{"close-with-reset", no_argument, ¶m.close_with_reset, 1},
{"debug", required_argument, 0, 'd'},
{"failure-status", required_argument, ¶m.failure_status, 0},
{"help", no_argument, 0, 'h'},
{"hog", no_argument, ¶m.hog, 1},
{"http-version", required_argument, (int *) ¶m.http_version, 0},
{"max-connections", required_argument, (int *) ¶m.max_conns, 0},
{"max-piped-calls", required_argument, (int *) ¶m.max_piped, 0},
{"method", required_argument, (int *) ¶m.method, 0},
{"myaddr", required_argument, (int *) ¶m.myaddr, 0},
{"no-host-hdr", no_argument, ¶m.no_host_hdr, 1},
{"num-calls", required_argument, (int *) ¶m.num_calls, 0},
{"num-conns", required_argument, (int *) ¶m.num_conns, 0},
{"period", required_argument, (int *) ¶m.rate.mean_iat, 0},
{"port", required_argument, (int *) ¶m.port, 0},
{"print-reply", optional_argument, ¶m.print_reply, 0},
{"print-request", optional_argument, ¶m.print_request, 0},
{"rate", required_argument, (int *) ¶m.rate, 0},
{"recv-buffer", required_argument, (int *) ¶m.recv_buffer_size, 0},
{"retry-on-failure", no_argument, ¶m.retry_on_failure, 1},
{"runtime", required_argument, (int *) ¶m.runtime, 0},
{"send-buffer", required_argument, (int *) ¶m.send_buffer_size, 0},
{"server", required_argument, (int *) ¶m.server, 0},
{"server-name", required_argument, (int *) ¶m.server_name, 0},
{"servers", required_argument, (int *) ¶m.servers, 0},
{"uri", required_argument, (int *) ¶m.uri, 0},
{"session-cookies", no_argument, (int *) ¶m.session_cookies, 1},
#ifdef HAVE_SSL
{"ssl", no_argument, ¶m.use_ssl, 1},
{"ssl-ciphers", required_argument, (int *) ¶m.ssl_cipher_list, 0},
{"tls-server-name", required_argument, (int *) ¶m.tls_server_name, 0},
{"ssl-no-reuse", no_argument, ¶m.ssl_reuse, 0},
{"ssl-certificate", required_argument, (int *) ¶m.ssl_cert, 0},
{"ssl-key", required_argument, (int *) ¶m.ssl_key, 0},
{"ssl-verify", optional_argument, (int *) ¶m.ssl_verify, 0},
{"ssl-ca-file", required_argument, (int *) ¶m.ssl_ca_file, 0},
{"ssl-ca-path", required_argument, (int *) ¶m.ssl_ca_path, 0},
{"ssl-protocol", required_argument, ¶m.ssl_protocol, 0},
#endif
{"think-timeout", required_argument, (int *) ¶m.think_timeout, 0},
{"timeout", required_argument, (int *) ¶m.timeout, 0},
{"use-timer-cache", no_argument, ¶m.use_timer_cache, 1},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"periodic-stats", no_argument, 0, 'n'},
{"wlog", required_argument, (int *) ¶m.wlog, 0},
{"wsess", required_argument, (int *) ¶m.wsess, 0},
{"wsesslog", required_argument, (int *) ¶m.wsesslog, 0},
{"wsesspage", required_argument, (int *) ¶m.wsesspage, 0},
{"wset", required_argument, (int *) ¶m.wset, 0},
{0, 0, 0, 0}
};
static void
usage(void)
{
printf("Usage: %s "
"[-hdvV] [--add-header S] [--burst-length N] [--client N/N]\n"
"\t[--close-with-reset] [--debug N] [--failure-status N]\n"
"\t[--help] [--hog] [--http-version S] [--max-connections N]\n"
"\t[--max-piped-calls N] [--method S] [--no-host-hdr]\n"
"\t[--num-calls N] [--num-conns N] [--session-cookies]\n"
"\t[--period [d|u|e]T1[,T2]|[v]T1,D1[,T2,D2]...[,Tn,Dn]\n"
"\t[--print-reply [header|body]] [--print-request [header|body]]\n"
"\t[--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]\n"
"\t[--server S|--servers file] [--server-name S] [--port N] [--uri S] "
"[--myaddr S]\n"
#ifdef HAVE_SSL
"\t[--ssl] [--ssl-ciphers L] [--ssl-no-reuse]\n"
"\t[--ssl-certificate file] [--ssl-key file]\n"
"\t[--ssl-ca-file file] [--ssl-ca-path path]\n"
"\t[--ssl-verify [yes|no]] [--ssl-protocol S]\n"
#endif
"\t[--think-timeout X] [--timeout X] [--verbose] [--version]\n"
"\t[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]\n"
"\t[--wset N,X]\n"
"\t[--runtime X]\n"
"\t[--use-timer-cache]\n"
"\t[--periodic-stats]\n", prog_name);
}
void
panic(const char *msg, ...)
{
va_list va;
va_start(va, msg);
vfprintf(stderr, msg, va);
va_end(va);
exit(1);
}
void
no_op(void)
{
}
static void
perf_sample(struct Timer *t, Any_Type regarg)
{
Any_Type callarg;
callarg.d = 1.0 / (timer_now() - perf_sample_start);
event_signal(EV_PERF_SAMPLE, 0, callarg);
/*
* prepare for next sample interval:
*/
perf_sample_start = timer_now();
if (timer_schedule(perf_sample, regarg, RATE_INTERVAL) == NULL)
panic("%s(%d): Received NULL from timer_schedule\n", __func__,
__LINE__);
}
int
main(int argc, char **argv)
{
extern Load_Generator uri_fixed, uri_wlog, uri_wset, conn_rate,
call_seq;
extern Load_Generator wsess, wsesslog, wsesspage, sess_cookie, misc;
extern Stat_Collector stats_basic, session_stat;
extern Stat_Collector stats_print_reply;
extern char *optarg;
int session_workload = 0;
int num_gen = 3;
Load_Generator *gen[5] = {
&call_seq,
&uri_fixed,
&conn_rate,
};
int num_stats = 1;
Stat_Collector *stat[3] = {
&stats_basic
};
int i, ch, longindex;
u_int minor, major;
char *end, *name;
Any_Type arg;
void *flag;
Time t;
int numRates = 0;
#ifdef __FreeBSD__
/*
* This works around a bug in earlier versions of FreeBSD that cause
* non-finite IEEE arithmetic to cause SIGFPE instead of the
* non-finite arithmetic as defined by IEEE.
*/
fpsetmask(0);
#endif
object_type_size[OBJ_CONN] = sizeof(Conn);
object_type_size[OBJ_CALL] = sizeof(Call);
param.http_version = 0x10001; /* default to HTTP/1.1 */
param.client.id = 0;
param.client.num_clients = 1;
param.port = -1;
param.uri = "/";
param.num_calls = 1;
param.burst_len = 1;
param.num_conns = 1;
/*
* These should be set to the minimum of 2*bandwidth*delay and the
* maximum request/reply size for single-call connections.
*/
param.send_buffer_size = 4096;
param.recv_buffer_size = 16384;
param.rate.dist = DETERMINISTIC;
#ifdef HAVE_SSL
param.ssl_reuse = 1;
param.ssl_verify = 0;
param.ssl_protocol = 0;
#endif
/*
* get program name:
*/
prog_name = strrchr(argv[0], '/');
if (prog_name)
++prog_name;
else
prog_name = argv[0];
/*
* process command line options:
*/
while ((ch =
getopt_long(argc, argv, "d:hvVn", longopts, &longindex)) >= 0) {
switch (ch) {
case 0:
flag = longopts[longindex].flag;
if (flag == ¶m.method)
param.method = optarg;
else if (flag == ¶m.additional_header)
param.additional_header = optarg;
else if (flag == ¶m.additional_header_file)
param.additional_header_file = optarg;
else if (flag == ¶m.num_calls) {
errno = 0;
param.num_calls = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal number of calls %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.http_version) {
if (sscanf(optarg, "%u.%u", &major, &minor) !=
2) {
fprintf(stderr,
"%s: illegal version number %s\n",
prog_name, optarg);
exit(1);
}
param.http_version =
(major << 16) | (minor & 0xffff);
} else if (flag == ¶m.myaddr) {
core_add_addresses(optarg);
} else if (flag == ¶m.burst_len) {
errno = 0;
param.burst_len = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end
|| param.burst_len < 1) {
fprintf(stderr,
"%s: illegal burst-length %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.failure_status) {
errno = 0;
param.failure_status =
strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end
|| param.failure_status <= 0) {
fprintf(stderr,
"%s: illegal failure status %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.num_conns) {
errno = 0;
param.num_conns = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal number of connections %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.max_conns) {
errno = 0;
param.max_conns = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal max. # of connection %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.max_piped) {
errno = 0;
param.max_piped = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal max. # of piped calls %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.port) {
errno = 0;
param.port = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end
|| (unsigned) param.port > 0xffff) {
fprintf(stderr,
"%s: illegal port number %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.print_request
|| flag == ¶m.print_reply) {
int val;
if (!optarg)
val = PRINT_HEADER | PRINT_BODY;
else
switch (tolower(optarg[0])) {
case 'h':
val = PRINT_HEADER;
break;
case 'b':
val = PRINT_BODY;
break;
default:
val =
PRINT_HEADER | PRINT_BODY;
break;
}
*(int *) flag = val;
} else if (flag == ¶m.rate) {
errno = 0;
param.rate.rate_param = strtod(optarg, &end);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal request rate %s\n",
prog_name, optarg);
exit(1);
}
if (param.rate.rate_param <= 0.0)
param.rate.mean_iat = 0.0;
else
param.rate.mean_iat =
1 / param.rate.rate_param;
param.rate.dist = DETERMINISTIC;
} else if (flag == ¶m.rate.mean_iat) { /* --period
*/
param.rate.dist = DETERMINISTIC;
if (!isdigit(*optarg))
switch (tolower(*optarg++)) {
case 'd':
param.rate.dist =
DETERMINISTIC;
break;
case 'u':
param.rate.dist = UNIFORM;
break;
case 'e':
param.rate.dist = EXPONENTIAL;
break;
case 'v':
param.rate.dist = VARIABLE;
break;
default:
fprintf(stderr,
"%s: illegal interarrival distribution "
"'%c' in %s\n",
prog_name, optarg[-1],
optarg - 1);
exit(1);
}
/*
* remaining params depend on selected
* distribution:
*/
errno = 0;
switch (param.rate.dist) {
case DETERMINISTIC:
case EXPONENTIAL:
param.rate.mean_iat =
strtod(optarg, &end);
if (errno == ERANGE || end == optarg
|| *end
|| param.rate.mean_iat < 0) {
fprintf(stderr,
"%s: illegal mean interarrival "
"time %s\n", prog_name,
optarg);
exit(1);
}
break;
case UNIFORM:
param.rate.min_iat =
strtod(optarg, &end);
if (errno == ERANGE || end == optarg
|| param.rate.min_iat < 0) {
fprintf(stderr,
"%s: illegal minimum interarrival "
"time %s\n", prog_name,
optarg);
exit(1);
}
if (*end != ',') {
fprintf(stderr,
"%s: minimum interarrival time not "
"followed by `,MAX_IAT' (rest: `%s')\n",
prog_name, end);
exit(1);
}
optarg = end + 1;
param.rate.max_iat =
strtod(optarg, &end);
if (errno == ERANGE || end == optarg
|| *end
|| param.rate.max_iat < 0) {
fprintf(stderr,
"%s: illegal request period %s\n",
prog_name, optarg);
exit(1);
}
param.rate.mean_iat =
0.5 * (param.rate.min_iat +
param.rate.max_iat);
break;
case VARIABLE:
while (1) {
if (numRates >= NUM_RATES) {
fprintf(stderr,
"%s: too many rates\n",
prog_name);
exit(1);
}
param.rate.iat[numRates] =
strtod(optarg, &end);
if (errno == ERANGE
|| end == optarg
|| param.rate.
iat[numRates] < 0) {
fprintf(stderr,
"%s: illegal minimum interarrival"
" time %s\n",
prog_name,
optarg);
exit(1);
}
if (*end != ',') {
fprintf(stderr,
"%s: interarrival time not "
"followed by `,duration' (rest: `%s')\n",
prog_name,
end);
exit(1);
}
optarg = end + 1;
param.rate.duration[numRates] =
strtod(optarg, &end);
if (errno == ERANGE
|| end == optarg
|| param.rate.
duration[numRates] < 0) {
fprintf(stderr,
"%s: illegal duration %s\n",
prog_name,
optarg);
exit(1);
}
if (numRates == 0)
param.rate.mean_iat =
param.rate.
iat[numRates];
else
param.rate.mean_iat +=
param.rate.
iat[numRates];
numRates++;
if (*end != ',') {
param.rate.numRates =
numRates;
break;
} else
optarg = end + 1;
}
param.rate.mean_iat /= numRates;
break;
default:
fprintf(stderr,
"%s: internal error parsing %s\n",
prog_name, optarg);
exit(1);
break;
}
param.rate.rate_param =
((param.rate.mean_iat <= 0.0)
? 0.0 : (1.0 / param.rate.mean_iat));
} else if (flag == ¶m.recv_buffer_size) {
errno = 0;
param.recv_buffer_size =
strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end
|| param.port > 0xffff) {
fprintf(stderr,
"%s: illegal receive buffer size %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.send_buffer_size) {
errno = 0;
param.send_buffer_size =
strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end
|| param.port > 0xffff) {
fprintf(stderr,
"%s: illegal send buffer size %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.client) {
errno = 0;
param.client.id = strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE) {
fprintf(stderr,
"%s: bad client id (rest: `%s')\n",
prog_name, optarg);
exit(1);
}
if (*end != '/') {
fprintf(stderr,
"%s: client id not followed by `/' (rest: `%s')\n",
prog_name, end);
exit(1);
}
optarg = end + 1;
param.client.num_clients =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE
|| param.client.id >=
param.client.num_clients) {
fprintf(stderr,
"%s: bad number of clients (rest: `%s')\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.server)
param.server = optarg;
else if (flag == ¶m.server_name)
param.server_name = optarg;
else if (flag == ¶m.servers)
param.servers = optarg;
#ifdef HAVE_SSL
else if (flag == ¶m.ssl_cipher_list)
param.ssl_cipher_list = optarg;
else if (flag == ¶m.ssl_cert)
param.ssl_cert = optarg;
else if (flag == ¶m.ssl_key)
param.ssl_key = optarg;
else if (flag == ¶m.ssl_verify)
{
if (!optarg)
param.ssl_verify = 1;
else
switch (tolower (optarg[0]))
{
case 'y': param.ssl_verify = 1; break;
case 'n': param.ssl_verify = 0; break;
default: param.ssl_verify = 0; break;
}
}
else if (flag == ¶m.ssl_ca_file)
param.ssl_ca_file = optarg;
else if (flag == ¶m.ssl_ca_path)
param.ssl_ca_path = optarg;
else if (flag == ¶m.ssl_protocol)
{
param.use_ssl = 1;
if (strcasecmp (optarg, "auto") == 0)
param.ssl_protocol = 0;
#ifndef OPENSSL_NO_SSL2
else if (strcasecmp (optarg, "SSLv2") == 0)
param.ssl_protocol = 2;
#endif
#ifndef OPENSSL_NO_SSL3
else if (strcasecmp (optarg, "SSLv3") == 0)
param.ssl_protocol = 3;
#endif
else if (strcasecmp (optarg, "TLSv1.0") == 0 || strcasecmp (optarg, "TLSv1_0") == 0 || strcasecmp (optarg, "TLSv1") == 0)
param.ssl_protocol = 4;
else if (strcasecmp (optarg, "TLSv1.1") == 0 || strcasecmp (optarg, "TLSv1_1") == 0)
param.ssl_protocol = 5;
else if (strcasecmp (optarg, "TLSv1.2") == 0 || strcasecmp (optarg, "TLSv1_2") == 0)
param.ssl_protocol = 6;
#ifdef TLS1_3_VERSION
else if (strcasecmp (optarg, "TLSv1.3") == 0 || strcasecmp (optarg, "TLSv1_3") == 0)
param.ssl_protocol = 7;
#endif
else
{
fprintf (stderr, "%s: illegal SSL protocol %s\n",
prog_name, optarg);
exit (1);
}
}
else if (flag == ¶m.tls_server_name)
{
if (param.ssl_protocol >= 4)
{
param.tls_server_name = optarg;
}
else
{
fprintf (stderr, "%s: Error setting the SNI (Server Name Indication) server name to %s. The --tls-server-name option can only be used if --ssl-protocol-version is set to TLSv1.0 and above.\n",
prog_name, optarg);
exit (1);
}
}
#endif
else if (flag == ¶m.uri)
param.uri = optarg;
else if (flag == ¶m.think_timeout) {
errno = 0;
param.think_timeout = strtod(optarg, &end);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal think timeout value %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.timeout) {
errno = 0;
param.timeout = strtod(optarg, &end);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr,
"%s: illegal connect timeout %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.runtime) {
errno = 0;
param.runtime = strtod(optarg, &end);
if (errno == ERANGE || end == optarg || *end ||
param.runtime <= 0.0) {
fprintf(stderr,
"%s: illegal runtime value %s\n",
prog_name, optarg);
exit(1);
}
} else if (flag == ¶m.wlog) {
gen[1] = &uri_wlog; /* XXX fix
* me---somehow */
param.wlog.do_loop = (*optarg == 'y')
|| (*optarg == 'Y');
param.wlog.file = optarg + 2;
} else if (flag == ¶m.wsess) {
num_gen = 2; /* XXX fix me---somehow */
gen[0] = &wsess;
stat[num_stats++] = &session_stat;
errno = 0;
name = "bad number of sessions (1st param)";
param.wsess.num_sessions =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE)
goto bad_wsess_param;
optarg = end + 1;
name =
"bad number of calls per session (2nd param)";
if (*end != ',')
goto bad_wsess_param;
optarg = end + 1;
param.wsess.num_calls =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE)
goto bad_wsess_param;
name = "bad user think time (3rd param)";
if (*end != ',')
goto bad_wsess_param;
optarg = end + 1;
param.wsess.think_time = strtod(optarg, &end);
if (end == optarg || errno == ERANGE
|| param.wsess.think_time < 0.0)
goto bad_wsess_param;
name = "extraneous parameter";
if (*end) {
bad_wsess_param:
fprintf(stderr,
"%s: %s in --wsess arg (rest: `%s')",
prog_name, name, end);
if (errno)
fprintf(stderr, ": %s",
strerror(errno));
fputc('\n', stderr);
exit(1);
}
session_workload = 1;
} else if (flag == ¶m.wsesspage) {
num_gen = 2; /* XXX fix me---somehow */
gen[0] = &wsesspage;
stat[num_stats++] = &session_stat;
errno = 0;
name = "bad number of sessions (1st param)";
param.wsesspage.num_sessions =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE)
goto bad_wsesspage_param;
optarg = end + 1;
name =
"bad number of user requests per session (2nd param)";
if (*end != ',')
goto bad_wsesspage_param;
optarg = end + 1;
param.wsesspage.num_reqs =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE)
goto bad_wsesspage_param;
name = "bad user think time (3rd param)";
if (*end != ',')
goto bad_wsesspage_param;
optarg = end + 1;
param.wsesspage.think_time =
strtod(optarg, &end);
if (end == optarg || errno == ERANGE
|| param.wsesspage.think_time < 0.0)
goto bad_wsesspage_param;
name = "extraneous parameter";
if (*end) {
bad_wsesspage_param:
fprintf(stderr,
"%s: %s in --wsesspage arg (rest: `%s')",
prog_name, name, end);
if (errno)
fprintf(stderr, ": %s",
strerror(errno));
fputc('\n', stderr);
exit(1);
}
session_workload = 1;
} else if (flag == ¶m.wsesslog) {
num_gen = 1; /* XXX fix me---somehow */
gen[0] = &wsesslog;
stat[num_stats++] = &session_stat;
errno = 0;
name = "bad number of sessions (1st param)";
param.wsesslog.num_sessions =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE)
goto bad_wsesslog_param;
optarg = end + 1;
name = "bad user think time (2nd param)";
if (*end != ',')
goto bad_wsesslog_param;
optarg = end + 1;
param.wsesslog.think_time =
strtod(optarg, &end);
if (end == optarg || errno == ERANGE
|| param.wsesslog.think_time < 0.0)
goto bad_wsesslog_param;
name = "bad session filename (3rd param)";
if (*end != ',')
goto bad_wsesslog_param;
optarg = end + 1;
/*
* simulate parsing of string
*/
param.wsesslog.file = optarg;
if ((end = strchr(optarg, ',')) == NULL)
/*
* must be last param, position end at
* final \0
*/
end = optarg + strlen(optarg);
else
/*
* terminate end of string
*/
*end++ = '\0';
optarg = end;
name = "extraneous parameter";
if (*end) {
bad_wsesslog_param:
fprintf(stderr,
"%s: %s in --wsesslog arg (rest: `%s')",
prog_name, name, end);
if (errno)
fprintf(stderr, ": %s",
strerror(errno));
fputc('\n', stderr);
exit(1);
}
session_workload = 1;
} else if (flag == ¶m.wset) {
gen[1] = &uri_wset; /* XXX fix
* me---somehow */
errno = 0;
name = "bad working set size (1st parameter)";
param.wset.num_files =
strtoul(optarg, &end, 0);
if (end == optarg || errno == ERANGE)
goto bad_wset_param;
name = "bad target miss rate (2nd parameter)";
if (*end != ',')
goto bad_wset_param;
optarg = end + 1;
param.wset.target_miss_rate =
strtod(optarg, &end);
if (end == optarg || errno == ERANGE
|| param.wset.target_miss_rate < 0.0
|| param.wset.target_miss_rate > 1.0)
goto bad_wset_param;
name = "extraneous parameter";
if (*end) {
bad_wset_param:
fprintf(stderr,
"%s: %s in --wset arg (rest: `%s')",
prog_name, name, optarg);
if (errno)
fprintf(stderr, ": %s",
strerror(errno));
fputc('\n', stderr);
exit(1);
}
}
break;
case 'd':
#ifdef DEBUG
errno = 0;
debug_level = strtoul(optarg, &end, 10);
if (errno == ERANGE || end == optarg || *end) {
fprintf(stderr, "%s: illegal debug level %s\n",
prog_name, optarg);
exit(1);
}
#else
fprintf(stderr,
"%s: sorry, need to recompile with -DDEBUG on...\n",
prog_name);
#endif
break;
case 'v':
++verbose;
break;
case 'V':
printf("%s: httperf-" VERSION " compiled " __DATE__
" with"
#ifndef DEBUG
"out"
#endif
" DEBUG with"
#ifndef TIME_SYSCALLS
"out"
#endif
" TIME_SYSCALLS.\n", prog_name);
exit(0);
case 'n':
++periodic_stats;
break;
case 'h':
usage();
exit(0);
case ':':
fprintf(stderr,
"%s: parameter missing for option %s\n",
prog_name, longopts[longindex].name);
exit(1);
case '?':
/*
* Invalid or ambiguous option name or extraneous
* parameter. getopt_long () already issued an
* explanation to the user, so all we do is call it
* quites.
*/
exit(1);
default:
fprintf(stderr,
"%s: getopt_long: unexpected value (%d)\n",
prog_name, ch);
exit(1);
}
}
if (param.server != NULL && param.servers != NULL) {
fprintf(stderr,
"%s: --server S or --servers file\n",
prog_name);
exit(-1);
}
if (param.server == NULL && param.servers == NULL)
param.server = "localhost";
#ifdef HAVE_SSL
if (param.use_ssl) {
char buf[1024];
if (param.port < 0)
param.port = 443;
SSL_library_init ();
SSL_load_error_strings ();
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
SSLeay_add_all_algorithms ();
#endif
switch (param.ssl_protocol)
{
/* 0/auto for highest available */
case 0:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
ssl_ctx = SSL_CTX_new (TLS_client_method ()); break;
#else
ssl_ctx = SSL_CTX_new (SSLv23_client_method ()); break;
#endif
#ifndef OPENSSL_NO_SSL2
/* 2/SSLv2 */
case 2:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
ssl_ctx = SSL_CTX_new (TLS_client_method ());
#if (OPENSSL_VERSION_NUMBER >= 0x10101000L)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3); break;
#else
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); break;
#endif
#else
ssl_ctx = SSL_CTX_new (SSLv2_client_method ()); break;
#endif
#endif
#ifndef OPENSSL_NO_SSL3
/* 3/SSLv3 */
case 3:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
ssl_ctx = SSL_CTX_new (TLS_client_method ());
SSL_CTX_set_min_proto_version(ssl_ctx, SSL3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, SSL3_VERSION);
break;
#else
ssl_ctx = SSL_CTX_new (SSLv3_client_method ()); break;
#endif
#endif
/* 4/TLSv1.0 */
case 4:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
ssl_ctx = SSL_CTX_new (TLS_client_method ());
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION);
break;
#else
ssl_ctx = SSL_CTX_new (TLSv1_client_method ());
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); break;
#endif
/* 5/TLSv1.1 */
case 5:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
ssl_ctx = SSL_CTX_new (TLS_client_method ());
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_1_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_1_VERSION);
break;
#else
ssl_ctx = SSL_CTX_new (TLSv1_client_method ());
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_2); break;
#endif
/* 6/TLSv1.2 */
case 6:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
ssl_ctx = SSL_CTX_new (TLS_client_method ());
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_2_VERSION);
break;
#else
ssl_ctx = SSL_CTX_new (TLSv1_client_method ());
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); break;
#endif
#ifdef TLS1_3_VERSION
/* 7/TLSv1.3 */
case 7:
ssl_ctx = SSL_CTX_new (TLS_client_method ());
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
break;
#endif
}
if (!ssl_ctx) {
ERR_print_errors_fp(stderr);
exit(-1);
}
memset(buf, 0, sizeof(buf));
RAND_seed(buf, sizeof(buf));
/* set server certificate verification */
if (param.ssl_verify == 1)
SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_PEER, NULL);
else
SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, NULL);
/* set default certificate authority verification path */
SSL_CTX_set_default_verify_paths (ssl_ctx);
/* load extra certificate authority files and paths */
if (param.ssl_ca_file || param.ssl_ca_path)
{
int ssl_err = SSL_CTX_load_verify_locations
(ssl_ctx, param.ssl_ca_file, param.ssl_ca_path);
if (DBG > 2)
fprintf (stderr, "SSL_CTX_load_verify_locations returned %d\n",
ssl_err);
}
/* if using client SSL authentication, load the certificate file */
if (param.ssl_cert)
{
int ssl_err = SSL_CTX_use_certificate_file
(ssl_ctx, param.ssl_cert, SSL_FILETYPE_PEM);
if (DBG > 2)
fprintf (stderr, "SSL_CTX_use_certificate_file returned %d\n",
ssl_err);
}
/* also load the client key */
if (param.ssl_key)
{
int ssl_err = SSL_CTX_use_PrivateKey_file
(ssl_ctx, param.ssl_key, SSL_FILETYPE_PEM);
if (DBG > 2)
fprintf (stderr, "SSL_CTX_use_PrivateKey_file returned %d\n",
ssl_err);
}
/* check client certificate and key consistency */
if (param.ssl_cert && param.ssl_key)
{
int ssl_err = SSL_CTX_check_private_key (ssl_ctx);
if (DBG > 2)
fprintf (stderr, "SSL_CTX_check_private_key returned %d\n",
ssl_err);
if (!ssl_err)
{
fprintf (stderr,
"SSL certificate and key failed consistency check\n");
exit (1);
}
}
}
#endif
if (param.port < 0)
param.port = 80;
if (param.print_reply || param.print_request)
stat[num_stats++] = &stats_print_reply;
if (param.session_cookies) {
if (!session_workload) {
fprintf(stderr,
"%s: --session-cookie requires session-oriented "
"workload (e.g., --wsess)\n", prog_name);
exit(-1);
}
gen[num_gen++] = &sess_cookie;
}
if (param.additional_header || param.additional_header_file ||
param.method)
gen[num_gen++] = &misc;
/*
* echo command invocation for logging purposes:
*/
printf("%s", prog_name);
if (verbose)
printf(" --verbose");
switch (param.print_reply) {
case 0:
break;
case PRINT_HEADER:
printf(" --print-reply=header");
break;
case PRINT_BODY:
printf(" --print-reply=body");
break;
default:
printf(" --print-reply");
break;
}
switch (param.print_request) {
case 0:
break;
case PRINT_HEADER:
printf(" --print-request=header");
break;
case PRINT_BODY:
printf(" --print-request=body");
break;
default:
printf(" --print-request");
break;
}
if (param.hog)
printf(" --hog");
if (param.close_with_reset)
printf(" --close-with-reset");
if (param.think_timeout > 0)
printf(" --think-timeout=%g", param.think_timeout);
if (param.timeout > 0)
printf(" --timeout=%g", param.timeout);
if (param.runtime > 0)
printf(" --runtime=%g", param.runtime);
printf(" --client=%u/%u", param.client.id, param.client.num_clients);
if (param.server)
printf(" --server=%s", param.server);
if (param.server_name)
printf(" --server_name=%s", param.server_name);
if (param.servers)
printf(" --servers=%s", param.servers);
if (param.port)
printf(" --port=%d", param.port);
if (param.uri)
printf(" --uri=%s", param.uri);
if (param.failure_status)
printf(" --failure-status=%u", param.failure_status);
if (param.http_version != 0x10001)
printf(" --http-version=%u.%u", param.http_version >> 16,
param.http_version & 0xffff);
if (param.max_conns)
printf(" --max-connections=%lu", param.max_conns);
if (param.max_piped)
printf(" --max-piped-calls=%lu", param.max_piped);
if (param.rate.rate_param > 0.0) {
switch (param.rate.dist) {
case DETERMINISTIC:
/*
* for backwards compatibility, continue to use
* --rate:
*/
printf(" --rate=%g", param.rate.rate_param);
break;
case UNIFORM:
printf(" --period=u%g,%g",
param.rate.min_iat, param.rate.max_iat);
break;
case EXPONENTIAL:
printf(" --period=e%g", param.rate.mean_iat);
break;
case VARIABLE:
{
int m;
printf(" --period=v");
for (m = 0; m < param.rate.numRates; m++) {
if (m != 0)
printf(",");
printf("%g,%g", param.rate.iat[m],
param.rate.duration[m]);
}
}
break;
default:
printf("--period=??");
break;
}
}
printf(" --send-buffer=%lu", param.send_buffer_size);
if (param.retry_on_failure)
printf(" --retry-on-failure");
printf(" --recv-buffer=%lu", param.recv_buffer_size);
if (param.session_cookies)
printf(" --session-cookies");
#ifdef HAVE_SSL
if (param.use_ssl)
printf(" --ssl");
if (param.ssl_cipher_list)
printf(" --ssl-ciphers=%s", param.ssl_cipher_list);
if (param.tls_server_name)
printf(" --tls-server-name=%s", param.tls_server_name);
if (!param.ssl_reuse)
printf(" --ssl-no-reuse");
if (param.ssl_cert) printf (" --ssl-cert=%s", param.ssl_cert);
if (param.ssl_key) printf (" --ssl-key=%s", param.ssl_key);
if (param.ssl_ca_file) printf (" --ssl-ca-file=%s", param.ssl_ca_file);
if (param.ssl_ca_path) printf (" --ssl-ca-path=%s", param.ssl_ca_path);
if (param.ssl_verify) printf (" --ssl-verify");
switch (param.ssl_protocol)
{
case 0: printf (" --ssl-protocol=auto"); break;
#ifndef OPENSSL_NO_SSL2
case 2: printf (" --ssl-protocol=SSLv2"); break;
#endif
#ifndef OPENSSL_NO_SSL3
case 3: printf (" --ssl-protocol=SSLv3"); break;
#endif
case 4: printf (" --ssl-protocol=TLSv1.0"); break;
case 5: printf (" --ssl-protocol=TLSv1.1"); break;
case 6: printf (" --ssl-protocol=TLSv1.2"); break;
#ifdef TLS1_3_VERSION
case 7: printf (" --ssl-protocol=TLSv1.3"); break;
#endif
}
#endif
if (param.additional_header)
printf(" --add-header='%s'", param.additional_header);
if (param.additional_header_file)
printf(" --add-header-file='%s'", param.additional_header_file);
if (param.method)
printf(" --method=%s", param.method);
if (param.use_timer_cache)
printf(" --use-timer-cache");
if (param.wsesslog.num_sessions) {
/*
* This overrides any --wsess, --num-conns, --num-calls,
* --burst-length and any uri generator
*/
printf(" --wsesslog=%u,%.3f,%s", param.wsesslog.num_sessions,
param.wsesslog.think_time, param.wsesslog.file);
} else if (param.wsesspage.num_sessions) {
printf(" --wsesspage=%u,%u,%.3f", param.wsesspage.num_sessions,
param.wsesspage.num_reqs, param.wsesspage.think_time);
} else {
if (param.wsess.num_sessions)
printf(" --wsess=%u,%u,%.3f", param.wsess.num_sessions,
param.wsess.num_calls, param.wsess.think_time);
else {
if (param.num_conns)
printf(" --num-conns=%lu", param.num_conns);
if (param.num_calls)
printf(" --num-calls=%lu", param.num_calls);
}
if (param.burst_len != 1)
printf(" --burst-length=%lu", param.burst_len);
if (param.wset.num_files)
printf(" --wset=%u,%.3f",
param.wset.num_files,
param.wset.target_miss_rate);
}
if (periodic_stats)
printf(" --periodic-stats");
printf("\n");
if (timer_init() == false) {
fprintf(stderr,
"%s: timer_init(): failed initialization (%d)\n",
prog_name, __LINE__);
exit(1);
}
core_init();
signal(SIGINT, (void (*)()) core_exit);
for (i = 0; i < num_stats; ++i)
(*stat[i]->init) ();
for (i = 0; i < num_gen; ++i)
(*gen[i]->init) ();
/*
* Update `now'. This is to keep things accurate even when some of
* the initialization routines take a long time to execute.
*/
timer_now_forced();
/*
* ensure that clients sample rates at different times:
*/
t = (param.client.id + 1.0) * RATE_INTERVAL / param.client.num_clients;
arg.l = 0;
timer_schedule(perf_sample, arg, t);
perf_sample_start = timer_now();
for (i = 0; i < num_gen; ++i)
(*gen[i]->start) ();
for (i = 0; i < num_stats; ++i)
(*stat[i]->start) ();
getrusage(RUSAGE_SELF, &test_rusage_start);
test_time_start = timer_now();
core_loop();
test_time_stop = timer_now();
getrusage(RUSAGE_SELF, &test_rusage_stop);
for (i = 0; i < num_stats; ++i)
(*stat[i]->stop) ();
for (i = 0; i < num_gen; ++i)
(*gen[i]->stop) ();
for (i = 0; i < num_stats; ++i)
(*stat[i]->dump) ();
timer_free_all();
return 0;
}
0707010000002D000081ED0000000000000000000000015FCD000200001A0B000000000000000000000000000000000000002900000000httperf-0.9.0+git.20201206/src/httperf.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef httperf_h
#define httperf_h
#include "config.h"
#define NELEMS(a) ((sizeof (a)) / sizeof ((a)[0]))
#define TV_TO_SEC(tv) ((tv).tv_sec + 1e-6*(tv).tv_usec)
#define NUM_RATES 16
typedef enum Dist_Type
{
DETERMINISTIC, /* also called fixed-rate */
UNIFORM, /* over interval [min_iat,max_iat) */
VARIABLE, /* allows varying input load */
EXPONENTIAL /* with mean mean_iat */
}
Dist_Type;
typedef struct Load_Generator
{
const char *name;
void (*init) (void);
void (*start) (void);
void (*stop) (void);
}
Load_Generator;
typedef struct Stat_Collector
{
const char *name;
/* START and STOP are timing sensitive, so they should be as short
as possible. More expensive stuff can be done during INIT and
DUMP. */
void (*init) (void);
void (*start) (void);
void (*stop) (void);
void (*dump) (void);
}
Stat_Collector;
typedef struct Rate_Info
{
Dist_Type dist; /* interarrival distribution */
double rate_param; /* 0 if mean_iat==0, else 1/mean_iat */
Time mean_iat; /* mean interarrival time */
Time min_iat; /* min interarrival time (for UNIFORM) */
Time max_iat; /* max interarrival time (for UNIFORM) */
int numRates; /* number of rates we want to use */
Time iat[NUM_RATES];
Time duration[NUM_RATES];
}
Rate_Info;
#define PRINT_HEADER (1 << 0)
#define PRINT_BODY (1 << 1)
typedef struct Cmdline_Params
{
int http_version; /* (default) HTTP protocol version */
const char *server; /* (default) hostname */
const char *server_name; /* fully qualified server name */
const char *servers;
int port; /* (default) server port */
const char *uri; /* (default) uri */
const char *myaddr;
Rate_Info rate;
Time timeout; /* watchdog timeout */
Time think_timeout; /* timeout for server think time */
Time runtime; /* how long to run the test */
u_long num_conns; /* # of connections to generate */
u_long num_calls; /* # of calls to generate per connection */
u_long burst_len; /* # of calls to burst back-to-back */
u_long max_piped; /* max # of piped calls per connection */
u_long max_conns; /* max # of connections per session */
int hog; /* client may hog as much resources as possible */
u_long send_buffer_size;
u_long recv_buffer_size;
int failure_status; /* status code that should be considered failure */
int retry_on_failure; /* when a call fails, should we retry? */
int close_with_reset; /* close connections with TCP RESET? */
int print_request; /* bit 0: print req headers, bit 1: print req body */
int print_reply; /* bit 0: print repl headers, bit 1: print repl body */
int session_cookies; /* handle set-cookies? (at the session level) */
int no_host_hdr; /* don't send Host: header in request */
#ifdef HAVE_SSL
int use_ssl; /* connect via SSL */
int ssl_reuse; /* reuse SSL Session ID */
int ssl_verify; /* whether to verify the server certificate */
int ssl_protocol; /* which SSL protocol to use */
const char *tls_server_name; /* TLS SNI (server name indication) */
const char *ssl_cipher_list; /* client's list of SSL cipher suites */
const char *ssl_cert; /* client certificate file name */
const char *ssl_key; /* client key file name */
const char *ssl_ca_file; /* certificate authority file */
const char *ssl_ca_path; /* certificate authority path */
#endif
int use_timer_cache;
const char *additional_header; /* additional request header(s) */
const char *additional_header_file;
const char *method; /* default call method */
struct
{
u_int id;
u_int num_clients;
}
client;
struct
{
char *file; /* name of the file where entries are */
char do_loop; /* boolean indicating if we want to loop on entries */
}
wlog;
struct
{
u_int num_sessions; /* # of sessions */
u_int num_calls; /* # of calls per session */
Time think_time; /* user think time between calls */
}
wsess;
struct
{
u_int num_sessions; /* # of sessions */
u_int num_reqs; /* # of user requests per session */
Time think_time; /* user think time between requests */
}
wsesspage;
struct
{
u_int num_sessions; /* # of user-sessions */
Time think_time; /* user think time between calls */
char *file; /* name of the file where session defs are */
}
wsesslog;
struct
{
u_int num_files;
double target_miss_rate;
}
wset;
}
Cmdline_Params;
extern const char *prog_name;
extern int verbose;
extern int periodic_stats;
extern Cmdline_Params param;
extern Time test_time_start;
extern Time test_time_stop;
extern struct rusage test_rusage_start;
extern struct rusage test_rusage_stop;
#ifdef HAVE_SSL
# include <openssl/ssl.h>
extern SSL_CTX *ssl_ctx;
#endif
#ifdef DEBUG
extern int debug_level;
# define DBG debug_level
#else
# define DBG 0
#endif
extern void panic (const char *msg, ...);
extern void no_op (void);
#endif /* httperf_h */
0707010000002E000081ED0000000000000000000000015FCD000200001A2D000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/idleconn.c/*
* Copyright (C) 2007, 2008 Ted Bullock <tbullock@comlore.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* For strrchr() */
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/time.h>
#include <generic_types.h>
#include <event.h>
#include <evdns.h>
#include <list.h>
/*
* Events allocated onto the heap
*/
static struct List *active_events = NULL;
static const char *prog_name = NULL;
static unsigned long num_conn = 0, num_closed = 0;
static struct timeval start_time;
static struct sockaddr_in server_addr;
static char *server = NULL;
static int desired = 0; /* Number of desired connections */
static int port = 0;
/*
* Frees the linked list of active event structures
*/
static void
cleanup()
{
if (active_events != NULL) {
while (!is_list_empty(active_events)) {
Any_Type a = list_pop(active_events);
struct event *evsock = (struct event *) a.vp;
event_del(evsock);
free(a.vp);
}
list_free(active_events);
}
}
/*
* Signal handler callback to be executed by event_dispatch upon receipt of
* SIGINT (usually Control-C
*/
void
sigint_exit(int fd, short event, void *arg)
{
struct event *signal_int = arg;
struct timeval stop_time;
double delta_t = 0;
gettimeofday(&stop_time, NULL);
delta_t = ((stop_time.tv_sec - start_time.tv_sec)
+ 1e-6 * (stop_time.tv_usec - start_time.tv_usec));
printf("%s: Total conns created = %lu; close() rate = %g conn/sec\n",
prog_name, num_conn, num_closed / delta_t);
#ifdef DEBUG
printf("%s: caught SIGINT... Exiting.\n", __func__);
#endif /* DEBUG */
evdns_shutdown(0);
event_del(signal_int);
cleanup();
}
/*
* Connection disconnect handler. Once a connection is dropped by the remote
* host, this function is executed and a new connection is established.
*
* Note, that this re-uses the event structure originally allocated in
* dns_lookup_callback
*/
void
reconnect(int sd, short event, void *arg)
{
struct sockaddr_in sin = server_addr;
struct event *evsock = (struct event *) arg;
close(sd);
num_closed++;
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
perror("connect");
cleanup();
exit(EXIT_FAILURE);
}
event_set(evsock, sd, EV_READ, reconnect, evsock);
event_add(evsock, NULL);
num_conn++;
}
/*
* For the explanation of these parameters, please refer to the libevent evdns
* callback API
*
* Upon receipt of a valid dns lookup result, attempts to open `desired`
* connections and allocates memory for the associated event structures
*/
void
dns_lookup_callback(int result, char type, int count, int ttl, void *addresses,
void *arg)
{
uint32_t i;
uint8_t oct[4];
struct in_addr *address_list = (struct in_addr *) addresses;
if (result != DNS_ERR_NONE) {
printf("DNS Lookup: result(%s)\n",
evdns_err_to_string(result));
exit(EXIT_FAILURE);
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = address_list[0];
/*
* Echo the resolved address
*/
printf("Resolved %s\n\t(%s)\n", server, inet_ntoa(address_list[0]));
/*
* Open the number of `desired` connections
*/
for (i = 0; i < desired; i++) {
struct sockaddr_in sin;
int sd = socket(AF_INET, SOCK_STREAM, 0);
struct event *evsock = NULL;
if (sd == -1) {
perror("socket");
continue;
}
sin = server_addr;
if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
perror("connect");
cleanup();
exit(EXIT_FAILURE);
}
evsock = (struct event *) malloc(sizeof(struct event));
if (evsock == NULL) {
cleanup();
exit(EXIT_FAILURE);
}
event_set(evsock, sd, EV_READ, reconnect, evsock);
list_push(active_events, (Any_Type) (void *) evsock);
num_conn++;
event_add(evsock, NULL);
}
}
int
main(int argc, char *argv[])
{
struct event signal_int;
active_events = list_create();
if (active_events == NULL)
goto init_failure;
event_init();
evdns_init();
/*
* Initalize one event
*/
event_set(&signal_int, SIGINT, EV_SIGNAL, sigint_exit, &signal_int);
event_add(&signal_int, NULL);
prog_name = strrchr(argv[0], '/');
if (prog_name)
++prog_name;
else
prog_name = argv[0];
if (argc != 4) {
fprintf(stderr, "Usage: `%s server port numidle'\n",
prog_name);
goto init_failure;
}
server = argv[1];
port = atoi(argv[2]);
desired = atoi(argv[3]);
printf("%s: Using libevent-%s for %s event notification system.\n"
"Control-c to exit\n\n", prog_name, event_get_version(),
event_get_method());
gettimeofday(&start_time, NULL);
evdns_resolve_ipv4(server, 0, dns_lookup_callback, NULL);
event_dispatch();
/*
* Event loop will only exit upon receiving SIGINT. Make sure we pass
* this on to the parent process
*/
if (signal(SIGINT, SIG_DFL) < 0)
perror("signal");
else
kill(getpid(), SIGINT);
init_failure:
cleanup();
exit(EXIT_FAILURE);
/*
* Should never reach here
*/
return 0;
}
0707010000002F000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/src/lib07070100000030000081ED0000000000000000000000015FCD000200000100000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/lib/Makefile.am# what flags you want to pass to the C compiler & linker
# AM_CFLAGS = -I$(srcdir)/..
AM_CFLAGS =
AM_LDFLAGS =
noinst_LIBRARIES = libutil.a
libutil_a_SOURCES = getopt.c getopt.h ssl_writev.c generic_types.h \
queue.c queue.h heap.c heap.h list.c list.h
07070100000031000081ED0000000000000000000000015FCD000200000101000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/READMEThis directory contains library-replacement functions that are needed
on some platforms. This code is copyright by the Free Software
Foundation and a copy of the license under which it is distributed can
be found in the file COPYING.LIB in this directory.
07070100000032000081A40000000000000000000000015FCD00020000076E000000000000000000000000000000000000003300000000httperf-0.9.0+git.20201206/src/lib/generic_types.h/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef generic_types_h
#define generic_types_h
#include <sys/types.h>
#include <stdbool.h>
typedef union {
char c;
int i;
long l;
u_char uc;
u_int ui;
u_long ul;
u_wide uw;
float f;
double d;
void *vp;
const void *cvp;
} Any_Type;
typedef double Time;
#endif /* generic_types_h */
07070100000033000081ED0000000000000000000000015FCD00020000387B000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/lib/getopt.c/*
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* MODIFICATION, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <errno.h>
#include "getopt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#define EMSG ""
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -1 if short_too is set and the option does not match long_options.
*/
static int
parse_long_options(char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too)
{
char *current_argv, *has_equal;
size_t current_argv_len;
int i, match;
current_argv = place;
match = -1;
optind++;
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
break;
}
/*
* If this is a known short option, don't allow
* a partial match of a single character.
*/
if (short_too && current_argv_len == 1)
continue;
if (match == -1) /* partial match */
match = i;
else {
/* ambiguous abbreviation */
if (PRINT_ERROR)
fprintf(stderr, "ambiguous option -- %.*s\n"
, (int)current_argv_len,
current_argv);
optopt = 0;
return (BADCH);
}
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
if (PRINT_ERROR)
fprintf(stderr, "option doesn't take an "
"argument -- %.*s\n",
(int)current_argv_len,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return (BADARG);
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':' indicates no error
* should be generated.
*/
if (PRINT_ERROR)
fprintf(stderr, "option requires an argument "
"-- %s\n",
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else { /* unknown option */
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR)
fprintf(stderr, "unknown option -- %s\n", current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
*/
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar, short_too;
static int posixly_correct = -1;
if (options == NULL)
return (-1);
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*/
if (posixly_correct == -1)
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
else if (*options == '-')
flags |= FLAG_ALLARGS;
if (*options == '+' || *options == '-')
options++;
/*
* XXX Some GNU programs (like cvs) set optind to 0 instead of
* XXX using optreset. Work around this braindamage.
*/
if (optind == 0)
optind = optreset = 1;
optarg = NULL;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if (*(place = nargv[optind]) != '-' ||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
place = EMSG; /* found non-option */
if (flags & FLAG_ALLARGS) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
/*
* If we have "-" do nothing, if "--" we are done.
*/
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
optind++;
place = EMSG;
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
/*
* Check long options if:
* 1) we were passed some
* 2) the arg is not just "-"
* 3) either the arg starts with -- we are getopt_long_only()
*/
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (flags & FLAG_LONGONLY))) {
short_too = 0;
if (*place == '-')
place++; /* --foo long option */
else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
idx, short_too);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(optchar == (int)'-' && *place != '\0') ||
(oli = strchr(options, optchar)) == NULL) {
/*
* If the user specified "-" and '-' isn't listed in
* options, return -1 (non-option) as per POSIX.
* Otherwise, it is an unknown option character (or ':').
*/
if (optchar == (int)'-' && *place == '\0')
return (-1);
if (!*place)
++optind;
if (PRINT_ERROR)
fprintf(stderr, "unknown option -- %c\n", optchar);
optopt = optchar;
return (BADCH);
}
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (*place) /* no space */
/* NOTHING */;
else if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
fprintf(stderr, "option requires an argument"
" -- %c\n", optchar);
optopt = optchar;
return (BADARG);
} else /* white space */
place = nargv[optind];
optchar = parse_long_options(nargv, options, long_options,
idx, 0);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
fprintf(stderr, "option requires an "
"argument -- %c\n", optchar);
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return (optchar);
}
/*
* getopt --
* Parse argc/argv argument vector.
*
* [eventually this will replace the BSD getopt]
*/
int
getopt(int nargc, char * const *nargv, const char *options)
{
/*
* We don't pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
* getopt_long_only --
* Parse argc/argv argument vector.
*/
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}
07070100000034000081ED0000000000000000000000015FCD000200000A59000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/lib/getopt.h/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
#ifndef _GETOPT_H_
#define _GETOPT_H_
/*
* GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
*/
#define no_argument 0
#define required_argument 1
#define optional_argument 2
struct option {
/* name of long option */
const char *name;
/*
* one of no_argument, required_argument, and optional_argument:
* whether option takes an argument
*/
int has_arg;
/* if not NULL, set *flag to val when option found */
int *flag;
/* if flag not NULL, value to set *flag to; else return value */
int val;
};
int getopt_long(int, char * const *, const char *,
const struct option *, int *);
int getopt_long_only(int, char * const *, const char *,
const struct option *, int *);
#ifndef _GETOPT_DEFINED_
#define _GETOPT_DEFINED_
int getopt(int, char * const *, const char *);
int getsubopt(char **, char * const *, char **);
extern char *optarg; /* getopt(3) external variables */
extern int opterr;
extern int optind;
extern int optopt;
extern int optreset;
extern char *suboptarg; /* getsubopt(3) external variable */
#endif
#endif /* !_GETOPT_H_ */
07070100000035000081A40000000000000000000000015FCD000200000F0A000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/heap.c/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdlib.h>
#include <generic_types.h>
#define Minimum_Heap_Size 10
struct Heap {
u_long array_size;
u_long num_elements;
bool (*compare) (Any_Type, Any_Type);
Any_Type storage[]; /* c99 Flexible Array Member */
};
struct Heap *
create_heap(u_long size, bool(*compare_callback) (Any_Type, Any_Type))
{
struct Heap *h;
if (size < Minimum_Heap_Size)
size = Minimum_Heap_Size;
/*
* We are using c99 Flexible Array Members so we can do this :)
*/
h = malloc(sizeof(struct Heap) + sizeof(Any_Type) * size);
if (!h)
return NULL;
h->array_size = size;
h->num_elements = 0;
h->compare = compare_callback;
return h;
}
bool
is_heap_empty(struct Heap * h)
{
return h->num_elements == 0;
}
bool
is_heap_full(struct Heap * h)
{
return h->array_size == h->num_elements;
}
u_long
num_heap_elements(struct Heap * h)
{
return h->num_elements;
}
void
free_heap(struct Heap *h)
{
if (h != NULL)
free(h);
}
#define PARENT(i) (i/2)
bool
insert(Any_Type a, struct Heap *h)
{
u_long i;
if (is_heap_full(h))
return false;
i = ++h->num_elements;
/*
* find the correct place to insert
*/
while ((i > 1) && (*h->compare) (h->storage[PARENT(i)], a)) {
h->storage[i] = h->storage[PARENT(i)];
i = PARENT(i);
}
h->storage[i] = a;
return true;
}
static void
percolate(struct Heap *h, u_long hole)
{
int child;
Any_Type dest_val = h->storage[hole];
for (; hole * 2 <= h->num_elements; hole = child) {
child = hole * 2;
if (child != h->num_elements
&& (*h->compare) (h->storage[child + 1],
h->storage[child + 1]))
child++;
if ((*h->compare) (h->storage[child + 1], dest_val))
h->storage[hole] = h->storage[child];
else
break;
}
h->storage[hole] = dest_val;
}
Any_Type
remove_min(struct Heap *h)
{
if (is_heap_empty(h)) {
Any_Type temp = {0};
return temp;
}
else {
Any_Type min = h->storage[1];
h->storage[1] = h->storage[h->num_elements--];
percolate(h, 1);
return min;
}
}
Any_Type
poll_min(struct Heap * h)
{
if (is_heap_empty(h)) {
Any_Type temp = {0};
return temp;
}
else
return h->storage[1];
}
void
heap_for_each(struct Heap *h, void (*action) (Any_Type))
{
for (u_long i = 1; i <= h->num_elements; i++) {
(*action) (h->storage[i]);
}
}
07070100000036000081A40000000000000000000000015FCD000200000842000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/heap.h/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef heap_h
#define heap_h
struct Heap;
typedef bool(*heap_compare) (Any_Type, Any_Type);
typedef void (*heap_for_each_action) (Any_Type);
struct Heap *create_heap(u_long, heap_compare);
bool is_heap_empty(struct Heap *);
bool is_heap_full(struct Heap *);
u_long num_heap_elements(struct Heap *);
void free_heap(struct Heap *);
bool insert(Any_Type, struct Heap *);
Any_Type remove_min(struct Heap *);
Any_Type poll_min(struct Heap *);
void heap_for_each(struct Heap *, heap_for_each_action);
#endif /* Heap_h */
07070100000037000081A40000000000000000000000015FCD000200000D2F000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/list.c/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdlib.h>
#include <generic_types.h>
struct Node {
Any_Type data;
struct Node *next;
};
struct List {
struct Node *dummy_head;
};
bool
is_list_empty(struct List *l)
{
return l->dummy_head->next == NULL;
}
struct List *
list_create()
{
struct List *l;
if ((l = malloc(sizeof(struct List))) == NULL)
goto create_error;
if ((l->dummy_head = malloc(sizeof(struct Node))) == NULL)
goto create_error;
l->dummy_head->next = NULL;
return l;
create_error:
if (l != NULL)
free(l);
return NULL;
}
void
list_free(struct List *l)
{
free(l->dummy_head);
l->dummy_head = NULL;
free(l);
}
bool
list_push(struct List *l, Any_Type data)
{
struct Node *n;
/*
* TODO: Implement caching so that we don't have to call
* malloc every time we push a new node onto the list
*/
if ((n = malloc(sizeof(struct Node))) == NULL) {
return false;
}
n->data = data;
n->next = l->dummy_head->next;
l->dummy_head->next = n;
return true;
}
Any_Type
list_top(struct List * l)
{
return l->dummy_head->next->data;
}
Any_Type
list_pop(struct List * l)
{
Any_Type data;
struct Node *n;
n = l->dummy_head->next;
data = l->dummy_head->next->data;
l->dummy_head->next = l->dummy_head->next->next;
/*
* TODO: As per above, implement caching here so that this memory
* does not have to be freed
*/
free(n);
return data;
}
void
list_remove_if_true(struct List *l, bool (*action) (Any_Type))
{
struct Node *n = l->dummy_head;
while (n->next != NULL) {
if ((*action) (n->next->data)) {
struct Node *oldnext = n->next;
n->next = n->next->next;
free(oldnext);
} else
n = n->next;
}
}
void
list_for_each(struct List *l, int (*action) (Any_Type))
{
struct Node *n = l->dummy_head->next;
while (n != NULL) {
(*action) (n->data);
n = n->next;
}
}
07070100000038000081A40000000000000000000000015FCD0002000007C3000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/list.h
/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef list_h
#define list_h
typedef bool (*list_action) (Any_Type);
struct List;
struct List *list_create();
void list_free(struct List *);
bool list_push(struct List *, Any_Type);
bool is_list_empty(struct List *);
Any_Type list_top(struct List *);
Any_Type list_pop(struct List *);
void list_remove_if_true(struct List *, list_action);
void list_for_each(struct List *, list_action);
#endif /* list_h */
07070100000039000081A40000000000000000000000015FCD000200000C24000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/lib/queue.c/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdlib.h>
#include <generic_types.h>
#define Minimum_Wheel_Size 10
struct Queue {
u_long wheel_size;
u_long num_elements;
u_long first_element;
u_long last_element;
Any_Type wheel[]; /* c99 Flexible Array Member */
};
static void
empty_queue(struct Queue *q)
{
q->num_elements = 0;
q->first_element = 1;
q->last_element = 0;
for (u_long i = 0; i < q->wheel_size; i++)
q->wheel[i].uw = 0;
}
struct Queue *
create_queue(u_long size)
{
struct Queue *q;
if (size < Minimum_Wheel_Size)
size = Minimum_Wheel_Size;
/*
* Again, we are using c99 Flexible Array Members so we can do this :)
*/
q = malloc(sizeof(struct Queue) + sizeof(Any_Type) * size);
if (q == NULL)
return NULL;
q->wheel_size = size;
empty_queue(q);
return q;
}
int
is_queue_empty(struct Queue *q)
{
return q->num_elements == 0;
}
int
is_queue_full(struct Queue *q)
{
return q->num_elements == q->wheel_size;
}
void
free_queue(struct Queue *q)
{
if (q != NULL) {
empty_queue(q);
free(q);
}
}
int
enqueue(Any_Type a, struct Queue *q)
{
if (is_queue_full(q))
return 0;
q->num_elements++;
if (++q->last_element == q->wheel_size)
q->last_element = 0;
q->wheel[q->last_element] = a;
return 1;
}
void
dequeue(struct Queue *q)
{
q->num_elements--;
if (++q->first_element == q->wheel_size)
q->first_element = 0;
}
Any_Type
get_front(struct Queue *q)
{
return q->wheel[q->first_element];
}
Any_Type
get_front_and_dequeue(struct Queue * q)
{
Any_Type a = get_front(q);
dequeue(q);
return a;
}
0707010000003A000081A40000000000000000000000015FCD0002000007B2000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/lib/queue.h/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef queue_h
#define queue_h
struct Queue;
struct Queue *create_queue(u_long);
int is_queue_empty(struct Queue *);
int is_queue_full(struct Queue *);
void free_queue(struct Queue *);
int enqueue(Any_Type, struct Queue *);
Any_Type get_front(struct Queue *);
void dequeue(struct Queue *);
Any_Type get_front_and_dequeue(struct Queue *);
void print_vp(struct Queue *);
#endif /* queue_h */
0707010000003B000081ED0000000000000000000000015FCD000200000CF8000000000000000000000000000000000000003000000000httperf-0.9.0+git.20201206/src/lib/ssl_writev.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company and Contributors listed in
AUTHORS file. Originally contributed by David Mosberger-Tang
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* This code is based on the generic writev() implementation of GNU
libc. It implements writev() simply in terms of write(). No
atomicity guarantees and performance isn't great either, but its
good enough for SSL purposes. */
#include "config.h"
#ifdef HAVE_OPENSSL_SSL_H
/* AIX requires this to be the first thing in the file. */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
# ifdef _AIX
#pragma alloca
# else
# ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
# endif
# endif
# endif
#else
/* stdlib.h provides the alloca functionality. */
#include <stdlib.h>
#endif
#include <string.h>
#include <generic_types.h>
#include <sys/uio.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
ssize_t
SSL_writev (SSL *ssl, const struct iovec *vector, int count)
{
char *buffer;
register char *bp;
size_t bytes, to_copy;
int i;
/* Find the total number of bytes to be written. */
bytes = 0;
for (i = 0; i < count; ++i)
bytes += vector[i].iov_len;
/* Allocate a temporary buffer to hold the data. */
buffer = (char *) alloca (bytes);
/* Copy the data into BUFFER. */
to_copy = bytes;
bp = buffer;
for (i = 0; i < count; ++i)
{
# define min(a, b) ((a) > (b) ? (b) : (a))
size_t copy = min (vector[i].iov_len, to_copy);
memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
bp += copy;
to_copy -= copy;
if (to_copy == 0)
break;
}
return SSL_write (ssl, buffer, bytes);
}
#endif /* HAVE_OPENSSL_SSL_H */
0707010000003C000081ED0000000000000000000000015FCD000200000DB0000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/localevent.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <localevent.h>
#define MAX_NUM_OPS 4
static const char * const event_name[EV_NUM_EVENT_TYPES] =
{
"EV_PERF_SAMPLE",
"EV_HOSTNAME_LOOKUP_START",
"EV_HOSTNAME_LOOKUP_STOP",
"EV_SESS_NEW",
"EV_SESS_FAILED",
"EV_SESS_DESTROYED",
"EV_CONN_NEW",
"EV_CONN_CONNECTING",
"EV_CONN_CONNECTED",
"EV_CONN_CLOSE",
"EV_CONN_DESTROYED",
"EV_CONN_FAILED",
"EV_CONN_TIMEOUT",
"EV_CALL_NEW",
"EV_CALL_ISSUE",
"EV_CALL_SEND_START",
"EV_CALL_SEND_RAW_DATA",
"EV_CALL_SEND_STOP",
"EV_CALL_RECV_START",
"EV_CALL_RECV_HDR",
"EV_CALL_RECV_RAW_DATA",
"EV_CALL_RECV_DATA",
"EV_CALL_RECV_FOOTER",
"EV_CALL_RECV_STOP",
"EV_CALL_DESTROYED"
};
typedef struct Event_Action
{
int num_ops;
struct closure
{
Event_Handler op;
Any_Type arg;
}
closure[MAX_NUM_OPS];
}
Event_Action;
static Event_Action action[EV_NUM_EVENT_TYPES] = {{0, }};
void
event_register_handler (Event_Type et, Event_Handler handler, Any_Type arg)
{
struct closure *c;
int n;
n = action[et].num_ops;
if (n >= MAX_NUM_OPS)
{
fprintf (stderr, "event_register_handler: sorry, attempted to register "
"more than %d handlers\n", MAX_NUM_OPS);
exit (1);
}
c = action[et].closure + n;
c->op = handler;
c->arg = arg;
action[et].num_ops = n + 1;
}
void
event_signal (Event_Type type, Object *obj, Any_Type arg)
{
Event_Action *act = action + type;
struct closure *c, *end;
if (DBG > 1)
{
assert (NELEMS (event_name) == EV_NUM_EVENT_TYPES);
fprintf (stderr, "event_signal: %s (obj=%p,arg=%lx)\n",
event_name[type], obj, arg.l);
}
end = &act->closure[act->num_ops];
for (c = &act->closure[0]; c < end; ++c)
(*c->op) (type, obj, c->arg, arg);
}
0707010000003D000081ED0000000000000000000000015FCD000200000ACB000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/localevent.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef localevent_h
#define localevent_h
typedef enum Event_Type
{
EV_NULL = 0,
EV_PERF_SAMPLE,
EV_HOSTNAME_LOOKUP_START,
EV_HOSTNAME_LOOKUP_STOP,
EV_SESS_NEW,
EV_SESS_FAILED,
EV_SESS_DESTROYED,
EV_CONN_NEW,
EV_CONN_CONNECTING,
EV_CONN_CONNECTED,
EV_CONN_CLOSE, /* connection closed */
EV_CONN_DESTROYED,
EV_CONN_FAILED, /* failed for reasons other than timeout */
EV_CONN_TIMEOUT,
EV_CALL_NEW,
EV_CALL_ISSUE,
EV_CALL_SEND_START,
EV_CALL_SEND_RAW_DATA,
EV_CALL_SEND_STOP,
EV_CALL_RECV_START,
EV_CALL_RECV_HDR,
EV_CALL_RECV_RAW_DATA,
EV_CALL_RECV_DATA,
EV_CALL_RECV_FOOTER,
EV_CALL_RECV_STOP,
EV_CALL_DESTROYED,
EV_NUM_EVENT_TYPES
}
Event_Type;
typedef struct Event
{
Event_Type type;
Object *obj;
Any_Type arg;
}
Event;
typedef void (*Event_Handler) (Event_Type type, Object *obj,
Any_Type registration_time_arg,
Any_Type signal_time_arg);
extern void event_register_handler (Event_Type et, Event_Handler handler,
Any_Type arg);
extern void event_signal (Event_Type type, Object *obj, Any_Type arg);
#endif /* localevent_h */
0707010000003E000081ED0000000000000000000000015FCD000200001095000000000000000000000000000000000000002800000000httperf-0.9.0+git.20201206/src/object.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <sess.h>
#define ALIGN(s) (((s) + sizeof (double) - 1) & ~(sizeof (double) - 1))
static size_t type_size[OBJ_NUM_TYPES] =
{
ALIGN (sizeof (Conn)),
ALIGN (sizeof (Call)),
ALIGN (sizeof (Sess))
};
struct free_list_el
{
struct free_list_el *next;
};
static struct free_list_el *free_list[OBJ_NUM_TYPES];
static void
object_destroy (Object *obj)
{
Object_Type type = obj->type;
struct free_list_el *el;
Event_Type event = EV_NULL;
Any_Type arg;
switch (type)
{
case OBJ_CALL:
call_deinit ((Call *) obj);
event = EV_CALL_DESTROYED;
break;
case OBJ_CONN:
conn_deinit ((Conn *) obj);
event = EV_CONN_DESTROYED;
break;
case OBJ_SESS:
sess_deinit ((Sess *) obj);
event = EV_SESS_DESTROYED;
break;
default:
assert (0);
break;
}
arg.l = 0;
event_signal (event, obj, arg);
/* Each object must be at least the size and alignment of "struct
free_list_el". Malloc takes care of returning properly aligned
objects. */
el = (struct free_list_el *) obj;
el->next = free_list[type];
free_list[type] = el;
}
size_t
object_expand (Object_Type type, size_t size)
{
size_t offset = type_size[type];
type_size[type] += ALIGN (size);
return offset;
}
Object *
object_new (Object_Type type)
{
struct free_list_el *el;
Event_Type event = EV_NULL;
size_t obj_size;
Any_Type arg;
Object *obj;
obj_size = type_size[type];
if (free_list[type])
{
el = free_list[type];
free_list[type] = el->next;
obj = (Object *) el;
}
else
{
obj = malloc (obj_size);
if (!obj)
{
fprintf (stderr, "%s.object_new: %s\n", prog_name, strerror (errno));
return 0;
}
}
memset (obj, 0, obj_size);
obj->ref_count = 1;
obj->type = type;
switch (type)
{
case OBJ_CALL:
call_init ((Call *) obj);
event = EV_CALL_NEW;
break;
case OBJ_CONN:
conn_init ((Conn *) obj);
event = EV_CONN_NEW;
break;
case OBJ_SESS:
sess_init ((Sess *) obj);
event = EV_SESS_NEW;
break;
default:
panic ("object_new: bad object type %d\n", type);
break;
}
arg.l = 0;
event_signal (event, obj, arg);
return obj;
}
void
object_dec_ref (Object *obj)
{
assert (obj->ref_count > 0);
if (--obj->ref_count == 0)
object_destroy (obj);
}
0707010000003F000081ED0000000000000000000000015FCD000200000AFA000000000000000000000000000000000000002800000000httperf-0.9.0+git.20201206/src/object.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef object_h
#define object_h
#ifdef DEBUG
# define object_is_conn(o) (((Object *) (o))->type == OBJ_CONN)
# define object_is_call(o) (((Object *) (o))->type == OBJ_CALL)
# define object_is_sess(o) (((Object *) (o))->type == OBJ_SESS)
#else
# define object_is_conn(o) 1
# define object_is_call(o) 1
# define object_is_sess(o) 1
#endif
typedef enum Object_Type
{
OBJ_CONN, /* connection object */
OBJ_CALL, /* call object */
OBJ_SESS, /* session object */
OBJ_NUM_TYPES
}
Object_Type;
typedef struct Object
{
Object_Type type;
u_int ref_count; /* # of references to this object */
}
Object;
/* This may be called during httperf initialize to reserve SIZE
"private" bytes in objects of type TYPE. The return value is the
offset of the private area. */
extern size_t object_expand (Object_Type type, size_t size);
/* Create a new object of type TYPE. */
extern Object *object_new (Object_Type type);
/* Create a new reference for object OBJ. */
#define object_inc_ref(o) (++(o)->ref_count)
/* Decrement the reference for object OBJ. If the reference count
reaches zero, the object's destroy function is called. */
extern void object_dec_ref (Object *obj);
#endif /* object_h */
07070100000040000081ED0000000000000000000000015FCD0002000007EF000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/sess.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#include "config.h"
#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <localevent.h>
#include <sess.h>
void
sess_init (Sess *sess)
{
}
void
sess_deinit (Sess *sess)
{
#ifdef HAVE_SSL
if (sess->ssl)
SSL_free (sess->ssl);
#endif
}
void
sess_failure (Sess *sess)
{
Any_Type arg;
if (sess->failed)
return;
sess->failed = 1;
arg.l = 0;
event_signal (EV_SESS_FAILED, (Object *) sess, arg);
sess_dec_ref (sess);
}
07070100000041000081ED0000000000000000000000015FCD0002000009DB000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/sess.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef sess_h
#define sess_h
#ifdef HAVE_SSL
# include <openssl/ssl.h>
#endif
/* Sessions are not used by the httperf's core itself, but they are
provided here for the benefit of workload generators that need such
a notion (e.g., to represent users). */
typedef struct Sess
{
Object obj;
#ifdef HAVE_SSL
SSL *ssl; /* SSL session (or NULL) */
#endif /* HAVE_SSL */
u_int failed : 1; /* did session fail? */
}
Sess;
/* Initialize the new session object S. */
extern void sess_init (Sess *s);
/* Destroy the session-specific state in session object S. */
extern void sess_deinit (Sess *s);
/* Session S failed. This causes EV_SESS_FAILED to be signalled and
gives up the caller's reference to S. */
extern void sess_failure (Sess *s);
#define sess_new() ((Sess *) object_new (OBJ_SESS))
#define sess_inc_ref(s) object_inc_ref ((Object *) (s))
#define sess_dec_ref(s) object_dec_ref ((Object *) (s))
#endif /* sess_h */
07070100000042000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002400000000httperf-0.9.0+git.20201206/src/stat07070100000043000081ED0000000000000000000000015FCD0002000000D6000000000000000000000000000000000000003000000000httperf-0.9.0+git.20201206/src/stat/Makefile.am# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(srcdir)/.. -I$(srcdir)/../gen -I$(srcdir)/../lib
noinst_LIBRARIES = libstat.a
libstat_a_SOURCES = basic.c sess_stat.c print_reply.c stats.h
07070100000044000081ED0000000000000000000000015FCD000200003DB3000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/stat/basic.c/*
* httperf -- a tool for measuring web server performance
* Copyright 2000-2007 Hewlett-Packard Company
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* Basic statistics collector.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <float.h>
#include <stdio.h>
#include <generic_types.h>
#include <sys/resource.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <stats.h>
/*
* Increase this if it does not cover at least 50% of all response times.
*/
#define MAX_LIFETIME 100.0 /* max. conn. lifetime in seconds */
#define BIN_WIDTH 1e-3
#define NUM_BINS ((u_int) (MAX_LIFETIME / BIN_WIDTH))
static struct {
u_long num_conns_issued; /* total # of connections * issued */
u_long num_replies[6]; /* completion count per status class */
u_long num_200; /* total # of 200 responses */
u_long num_302; /* total # of 302 responses */
u_long num_client_timeouts; /* # of client timeouts */
u_long num_sock_fdunavail; /* # of times out of *
* filedescriptors */
u_long num_sock_ftabfull; /* # of times file table was full */
u_long num_sock_refused; /* # of ECONNREFUSED */
u_long num_sock_reset; /* # of ECONNRESET */
u_long num_sock_timeouts; /* # of ETIMEDOUT */
u_long num_sock_addrunavail; /* # of EADDRNOTAVAIL */
u_long num_other_errors; /* # of other errors */
u_long max_conns; /* max # of concurrent connections */
u_long num_lifetimes;
Time conn_lifetime_sum; /* sum of connection lifetimes */
Time conn_lifetime_sum2; /* sum of connection lifetimes
* squared */
Time conn_lifetime_min; /* minimum connection lifetime */
Time conn_lifetime_max; /* maximum connection lifetime */
u_long num_reply_rates;
Time reply_rate_sum;
Time reply_rate_sum2;
Time reply_rate_min;
Time reply_rate_max;
u_long num_connects; /* # of completed connect()s */
Time conn_connect_sum; /* sum of connect times */
u_long num_responses;
Time call_response_sum; /* sum of response times */
Time call_xfer_sum; /* sum of response times */
u_long num_sent; /* # of requests sent */
size_t req_bytes_sent;
u_long num_received; /* # of replies received */
u_wide hdr_bytes_received; /* sum of all header bytes */
u_wide reply_bytes_received; /* sum of all data bytes */
u_wide footer_bytes_received; /* sum of all footer bytes */
u_int conn_lifetime_hist[NUM_BINS]; /* histogram of
* connection lifetimes */
} basic;
static u_long num_active_conns;
static u_long num_replies; /* # of replies received in this interval */
static void
perf_sample(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
Time weight = call_arg.d;
double rate;
assert(et == EV_PERF_SAMPLE);
rate = weight * num_replies;
if (verbose)
printf("reply-rate = %-8.1f\n", rate);
basic.reply_rate_sum += rate;
basic.reply_rate_sum2 += SQUARE(rate);
if (rate < basic.reply_rate_min)
basic.reply_rate_min = rate;
if (rate > basic.reply_rate_max)
basic.reply_rate_max = rate;
++basic.num_reply_rates;
/*
* prepare for next sample interval:
*/
num_replies = 0;
}
static void
conn_timeout(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
assert(et == EV_CONN_TIMEOUT);
++basic.num_client_timeouts;
}
static void
conn_fail(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
static int first_time = 1;
int err = call_arg.i;
assert(et == EV_CONN_FAILED);
switch (err) {
#ifdef __linux__
case EINVAL: /* Linux has a strange way of saying "out of
* * fds"... */
#endif
case EMFILE:
++basic.num_sock_fdunavail;
break;
case ENFILE:
++basic.num_sock_ftabfull;
break;
case ECONNREFUSED:
++basic.num_sock_refused;
break;
case ETIMEDOUT:
++basic.num_sock_timeouts;
break;
case EPIPE:
case ECONNRESET:
++basic.num_sock_reset;
break;
default:
if (first_time) {
first_time = 0;
fprintf(stderr,
"%s: connection failed with unexpected error %d\n",
prog_name, errno);
}
++basic.num_other_errors;
break;
}
}
static void
conn_created(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type c_arg)
{
++num_active_conns;
if (num_active_conns > basic.max_conns)
basic.max_conns = num_active_conns;
}
static void
conn_connecting(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type c_arg)
{
Conn *s = (Conn *) obj;
assert(et == EV_CONN_CONNECTING && object_is_conn(s));
s->basic.time_connect_start = timer_now();
++basic.num_conns_issued;
}
static void
conn_connected(Event_Type et, Object * obj, Any_Type reg_arg,
Any_Type call_arg)
{
Conn *s = (Conn *) obj;
assert(et == EV_CONN_CONNECTED && object_is_conn(s));
basic.conn_connect_sum += timer_now() - s->basic.time_connect_start;
++basic.num_connects;
}
static void
conn_destroyed(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type c_arg)
{
Conn *s = (Conn *) obj;
Time lifetime;
u_int bin;
assert(et == EV_CONN_DESTROYED && object_is_conn(s)
&& num_active_conns > 0);
if (s->basic.num_calls_completed > 0) {
lifetime = timer_now() - s->basic.time_connect_start;
basic.conn_lifetime_sum += lifetime;
basic.conn_lifetime_sum2 += SQUARE(lifetime);
if (lifetime < basic.conn_lifetime_min)
basic.conn_lifetime_min = lifetime;
if (lifetime > basic.conn_lifetime_max)
basic.conn_lifetime_max = lifetime;
++basic.num_lifetimes;
bin = lifetime * NUM_BINS / MAX_LIFETIME;
if (bin >= NUM_BINS)
bin = NUM_BINS;
++basic.conn_lifetime_hist[bin];
}
--num_active_conns;
}
static void
send_start(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
Call *c = (Call *) obj;
assert(et == EV_CALL_SEND_START && object_is_call(c));
c->basic.time_send_start = timer_now();
}
static void
send_stop(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
Call *c = (Call *) obj;
assert(et == EV_CALL_SEND_STOP && object_is_call(c));
basic.req_bytes_sent += c->req.size;
++basic.num_sent;
}
static void
recv_start(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
Call *c = (Call *) obj;
Time now;
assert(et == EV_CALL_RECV_START && object_is_call(c));
now = timer_now();
basic.call_response_sum += now - c->basic.time_send_start;
c->basic.time_recv_start = now;
++basic.num_responses;
if (periodic_stats) {
if (c->reply.status == 200)
++basic.num_200;
if (c->reply.status == 302)
++basic.num_302;
}
}
static void
recv_stop(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
Call *c = (Call *) obj;
int index;
assert(et == EV_CALL_RECV_STOP && object_is_call(c));
assert(c->basic.time_recv_start > 0);
basic.call_xfer_sum += timer_now() - c->basic.time_recv_start;
basic.hdr_bytes_received += c->reply.header_bytes;
basic.reply_bytes_received += c->reply.content_bytes;
basic.footer_bytes_received += c->reply.footer_bytes;
index = (c->reply.status / 100);
assert((unsigned) index < NELEMS(basic.num_replies));
++basic.num_replies[index];
++num_replies;
++c->conn->basic.num_calls_completed;
}
static void
one_second_timer(struct Timer *t, Any_Type arg)
{
static u_long prev200 = 0;
static u_long prev302 = 0;
printf("[%.6f s] 200=%lu, 302=%lu\n",
timer_now() - test_time_start,
basic.num_200 - prev200,
basic.num_302 - prev302);
prev200 = basic.num_200;
prev302 = basic.num_302;
timer_schedule(one_second_timer, arg, 1);
}
static void
init(void)
{
Any_Type arg;
basic.conn_lifetime_min = DBL_MAX;
basic.reply_rate_min = DBL_MAX;
arg.l = 0;
event_register_handler(EV_PERF_SAMPLE, perf_sample, arg);
event_register_handler(EV_CONN_FAILED, conn_fail, arg);
event_register_handler(EV_CONN_TIMEOUT, conn_timeout, arg);
event_register_handler(EV_CONN_NEW, conn_created, arg);
event_register_handler(EV_CONN_CONNECTING, conn_connecting, arg);
event_register_handler(EV_CONN_CONNECTED, conn_connected, arg);
event_register_handler(EV_CONN_DESTROYED, conn_destroyed, arg);
event_register_handler(EV_CALL_SEND_START, send_start, arg);
event_register_handler(EV_CALL_SEND_STOP, send_stop, arg);
event_register_handler(EV_CALL_RECV_START, recv_start, arg);
event_register_handler(EV_CALL_RECV_STOP, recv_stop, arg);
if (periodic_stats)
timer_schedule(one_second_timer, arg, 1);
}
static void
dump(void)
{
Time conn_period = 0.0, call_period = 0.0;
Time conn_time = 0.0, resp_time = 0.0, xfer_time = 0.0;
Time call_size = 0.0, hdr_size = 0.0, reply_size =
0.0, footer_size = 0.0;
Time lifetime_avg = 0.0, lifetime_stddev =
0.0, lifetime_median = 0.0;
double reply_rate_avg = 0.0, reply_rate_stddev = 0.0;
int i;
u_long total_replies = 0;
Time delta, user, sys;
u_wide total_size;
Time time;
u_int n;
for (i = 1; i < NELEMS(basic.num_replies); ++i)
total_replies += basic.num_replies[i];
delta = test_time_stop - test_time_start;
if (verbose > 1) {
printf("\nConnection lifetime histogram (time in ms):\n");
for (i = 0; i < NUM_BINS; ++i)
if (basic.conn_lifetime_hist[i]) {
if (i > 0 && basic.conn_lifetime_hist[i - 1] == 0)
printf("%14c\n", ':');
time = (i + 0.5) * BIN_WIDTH;
printf("%16.1f %u\n", 1e3 * time,
basic.conn_lifetime_hist[i]);
}
}
printf("\nTotal: connections %lu requests %lu replies %lu "
"test-duration %.3f s\n",
basic.num_conns_issued, basic.num_sent, total_replies, delta);
putchar('\n');
if (basic.num_conns_issued)
conn_period = delta / basic.num_conns_issued;
printf("Connection rate: %.1f conn/s (%.1f ms/conn, "
"<=%lu concurrent connections)\n",
basic.num_conns_issued / delta, 1e3 * conn_period,
basic.max_conns);
if (basic.num_lifetimes > 0) {
lifetime_avg = (basic.conn_lifetime_sum / basic.num_lifetimes);
if (basic.num_lifetimes > 1)
lifetime_stddev = STDDEV(basic.conn_lifetime_sum,
basic.conn_lifetime_sum2,
basic.num_lifetimes);
n = 0;
for (i = 0; i < NUM_BINS; ++i) {
n += basic.conn_lifetime_hist[i];
if (n >= 0.5 * basic.num_lifetimes) {
lifetime_median = (i + 0.5) * BIN_WIDTH;
break;
}
}
}
printf("Connection time [ms]: min %.1f avg %.1f max %.1f median %.1f "
"stddev %.1f\n",
basic.num_lifetimes > 0 ? 1e3 * basic.conn_lifetime_min : 0.0,
1e3 * lifetime_avg,
1e3 * basic.conn_lifetime_max, 1e3 * lifetime_median,
1e3 * lifetime_stddev);
if (basic.num_connects > 0)
conn_time = basic.conn_connect_sum / basic.num_connects;
printf("Connection time [ms]: connect %.1f\n", 1e3 * conn_time);
printf("Connection length [replies/conn]: %.3f\n",
basic.num_lifetimes > 0
? total_replies / (double) basic.num_lifetimes : 0.0);
putchar('\n');
if (basic.num_sent > 0)
call_period = delta / basic.num_sent;
printf("Request rate: %.1f req/s (%.1f ms/req)\n",
basic.num_sent / delta, 1e3 * call_period);
if (basic.num_sent)
call_size = basic.req_bytes_sent / basic.num_sent;
printf("Request size [B]: %.1f\n", call_size);
putchar('\n');
if (basic.num_reply_rates > 0) {
reply_rate_avg = (basic.reply_rate_sum / basic.num_reply_rates);
if (basic.num_reply_rates > 1)
reply_rate_stddev = STDDEV(basic.reply_rate_sum,
basic.reply_rate_sum2,
basic.num_reply_rates);
}
printf
("Reply rate [replies/s]: min %.1f avg %.1f max %.1f stddev %.1f "
"(%lu samples)\n",
basic.num_reply_rates > 0 ? basic.reply_rate_min : 0.0,
reply_rate_avg, basic.reply_rate_max, reply_rate_stddev,
basic.num_reply_rates);
if (basic.num_responses > 0)
resp_time = basic.call_response_sum / basic.num_responses;
if (total_replies > 0)
xfer_time = basic.call_xfer_sum / total_replies;
printf("Reply time [ms]: response %.1f transfer %.1f\n",
1e3 * resp_time, 1e3 * xfer_time);
if (total_replies) {
hdr_size = basic.hdr_bytes_received / total_replies;
reply_size = basic.reply_bytes_received / total_replies;
footer_size = basic.footer_bytes_received / total_replies;
}
printf("Reply size [B]: header %.1f content %.1f footer %.1f "
"(total %.1f)\n", hdr_size, reply_size, footer_size,
hdr_size + reply_size + footer_size);
printf("Reply status: 1xx=%lu 2xx=%lu 3xx=%lu 4xx=%lu 5xx=%lu\n",
basic.num_replies[1], basic.num_replies[2],
basic.num_replies[3], basic.num_replies[4], basic.num_replies[5]);
putchar('\n');
if (periodic_stats) {
printf("Periodic stats: 200=%lu 302=%lu\n", basic.num_200, basic.num_302);
putchar('\n');
}
user = (TV_TO_SEC(test_rusage_stop.ru_utime)
- TV_TO_SEC(test_rusage_start.ru_utime));
sys = (TV_TO_SEC(test_rusage_stop.ru_stime)
- TV_TO_SEC(test_rusage_start.ru_stime));
printf
("CPU time [s]: user %.2f system %.2f (user %.1f%% system %.1f%% "
"total %.1f%%)\n", user, sys, 100.0 * user / delta,
100.0 * sys / delta, 100.0 * (user + sys) / delta);
total_size = (basic.req_bytes_sent
+ basic.hdr_bytes_received + basic.reply_bytes_received);
printf("Net I/O: %.1f KB/s (%.1f*10^6 bps)\n",
total_size / delta / 1024.0, 8e-6 * total_size / delta);
putchar('\n');
printf("Errors: total %lu client-timo %lu socket-timo %lu "
"connrefused %lu connreset %lu\n"
"Errors: fd-unavail %lu addrunavail %lu ftab-full %lu other %lu\n",
(basic.num_client_timeouts + basic.num_sock_timeouts
+ basic.num_sock_fdunavail + basic.num_sock_ftabfull
+ basic.num_sock_refused + basic.num_sock_reset
+ basic.num_sock_addrunavail + basic.num_other_errors),
basic.num_client_timeouts, basic.num_sock_timeouts,
basic.num_sock_refused, basic.num_sock_reset,
basic.num_sock_fdunavail, basic.num_sock_addrunavail,
basic.num_sock_ftabfull, basic.num_other_errors);
}
Stat_Collector stats_basic = {
"Basic statistics",
init,
no_op,
no_op,
dump
};
07070100000045000081ED0000000000000000000000015FCD000200001C9D000000000000000000000000000000000000003200000000httperf-0.9.0+git.20201206/src/stat/print_reply.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* This statistics collector simply prints the replies received from
the server. */
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
typedef struct Call_Private_Data
{
int done_with_reply_hdr; /* are we done printing reply header? */
size_t size; /* number of bytes allocated for "line" buffer */
size_t len; /* current length of line buffer */
char *line; /* line buffer */
}
Call_Private_Data;
#define CALL_PRIVATE_DATA(c) \
((Call_Private_Data *) ((char *)(c) + call_private_data_offset))
static size_t call_private_data_offset = -1;
static void
flush_print_buf (Call *call, const char *prefix)
{
Call_Private_Data *priv = CALL_PRIVATE_DATA (call);
if (priv->len)
printf ("%s%ld:%.*s\n", prefix, call->id, (int) priv->len, priv->line);
priv->len = 0;
}
static void
print_buf (Call *call, const char *prefix, const char *buf, int len)
{
Call_Private_Data *priv = CALL_PRIVATE_DATA (call);
const char *eol, *end;
size_t line_len;
for (end = buf + len; buf < end; buf += line_len)
{
line_len = (end - buf);
eol = strchr (buf, '\n');
if (eol)
{
/* got a complete line: print it */
line_len = eol - buf;
printf ("%s%ld:", prefix, call->id);
if (priv->len)
printf ("%.*s", (int) priv->len, priv->line);
priv->len = 0;
if (line_len > 0)
printf ("%.*s", (int) line_len, buf);
putchar ('\n');
++line_len; /* skip over newline */
}
else
{
/* got a partial line: buffer it */
if (priv->len + line_len > priv->size)
{
priv->size = priv->len + line_len;
if (priv->line)
priv->line = realloc (priv->line, priv->size);
else
priv->line = malloc (priv->size);
if (!priv->line)
{
fprintf (stderr, "%s.print_buf: Out of memory\n", prog_name);
exit (1);
}
}
memcpy (priv->line + priv->len, buf, line_len);
priv->len += line_len;
}
}
}
static void
print_request (Call *call)
{
size_t hdr_len, h_len, b_len;
int i, first, end;
char *hdr;
first = IE_CONTENT;
end = IE_CONTENT;
if ((param.print_request & PRINT_HEADER) != 0)
first = IE_METHOD;
if ((param.print_request & PRINT_BODY) != 0)
end = IE_LEN;
for (i = first; i < end; ++i)
{
hdr = call->req.iov[i].iov_base;
hdr_len = call->req.iov[i].iov_len;
if (hdr_len)
print_buf (call, (i < IE_CONTENT) ? "SH" : "SB", hdr, hdr_len);
}
for (h_len = 0, i = IE_METHOD; i < IE_CONTENT; ++i)
h_len += call->req.iov[i].iov_len;
for (b_len = 0, i = IE_CONTENT; i < IE_LEN; ++i)
b_len += call->req.iov[i].iov_len;
printf ("SS%ld: header %ld content %ld\n",
call->id, (long) h_len, (long) b_len);
}
static void
print_reply_hdr (Call *call, const char *buf, int len)
{
Call_Private_Data *priv = CALL_PRIVATE_DATA (call);
const char *eoh;
if (len <= 0 || priv->done_with_reply_hdr)
return;
eoh = strstr (buf, "\r\n\r\n");
if (eoh)
{
priv->done_with_reply_hdr = 1;
eoh += 4;
}
else
{
/* no CRLFCRLF: non-conforming server */
eoh = strstr (buf, "\n\n");
if (eoh)
{
priv->done_with_reply_hdr = 1;
eoh += 2;
}
else
eoh = buf + len;
}
print_buf (call, "RH", buf, eoh - buf);
}
static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call_Private_Data *priv;
Call *call;
assert (et == EV_CALL_DESTROYED && object_is_call (obj));
call = (Call *) obj;
priv = CALL_PRIVATE_DATA (call);
if (priv->line)
free (priv->line);
}
static void
send_raw_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call *call;
assert (et == EV_CALL_SEND_RAW_DATA && object_is_call (obj));
call = (Call *) obj;
print_request (call);
}
static void
recv_raw_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
struct iovec *iov;
Call *call;
assert (et == EV_CALL_RECV_RAW_DATA && object_is_call (obj));
call = (Call *) obj;
iov = callarg.vp;
print_reply_hdr (call, iov->iov_base, iov->iov_len);
}
static void
recv_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
struct iovec *iov;
Call *call;
assert (et == EV_CALL_RECV_DATA && object_is_call (obj));
call = (Call *) obj;
iov = callarg.vp;
print_buf (call, "RB", iov->iov_base, iov->iov_len);
}
static void
recv_stop (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Call *call;
assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
call = (Call *) obj;
flush_print_buf (call, "RB");
printf ("RS%ld: header %ld content %ld footer %ld\n",
call->id, (long) call->reply.header_bytes,
(long) call->reply.content_bytes, (long) call->reply.footer_bytes);
}
static void
init (void)
{
Any_Type arg;
call_private_data_offset = object_expand (OBJ_CALL,
sizeof (Call_Private_Data));
arg.l = 0;
if ((param.print_request & (PRINT_HEADER | PRINT_BODY)) != 0)
event_register_handler (EV_CALL_SEND_RAW_DATA, send_raw_data, arg);
if ((param.print_reply & PRINT_HEADER) != 0)
event_register_handler (EV_CALL_RECV_RAW_DATA, recv_raw_data, arg);
if ((param.print_reply & PRINT_BODY) != 0)
event_register_handler (EV_CALL_RECV_DATA, recv_data, arg);
if ((param.print_reply & (PRINT_HEADER | PRINT_BODY)) != 0)
event_register_handler (EV_CALL_RECV_STOP, recv_stop, arg);
event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}
Stat_Collector stats_print_reply =
{
"Reply printer",
init,
no_op,
no_op,
no_op
};
07070100000046000081ED0000000000000000000000015FCD000200001D1A000000000000000000000000000000000000003000000000httperf-0.9.0+git.20201206/src/stat/sess_stat.c/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
/* Session statistics collector. */
#include "config.h"
#include <assert.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <session.h>
#include <stats.h>
static struct
{
u_int num_rate_samples;
u_int num_completed_since_last_sample;
Time rate_sum;
Time rate_sum2;
Time rate_min;
Time rate_max;
u_int num_completed;
Time lifetime_sum;
u_int num_failed;
Time failtime_sum;
u_int num_conns; /* total # of connections on successful sessions */
/* session-length histogram: */
u_int longest_session;
u_int len_hist_alloced;
u_int *len_hist;
}
st;
#define SESS_PRIVATE_DATA(c) \
((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
typedef struct Sess_Private_Data
{
u_int num_calls_completed; /* how many calls completed? */
u_int num_conns; /* # of connections on this session */
Time birth_time; /* when this session got created */
}
Sess_Private_Data;
static size_t sess_private_data_offset = -1;
static void
perf_sample (Event_Type et, Object *obj, Any_Type reg_arg, Any_Type call_arg)
{
Time weight = call_arg.d;
double rate;
assert (et == EV_PERF_SAMPLE);
rate = weight*st.num_completed_since_last_sample;
st.num_completed_since_last_sample = 0;
if (verbose)
printf ("session-rate = %-8.1f\n", rate);
++st.num_rate_samples;
st.rate_sum += rate;
st.rate_sum2 += SQUARE (rate);
if (rate < st.rate_min)
st.rate_min = rate;
if (rate > st.rate_max)
st.rate_max = rate;
}
static void
sess_created (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
assert (et == EV_SESS_NEW && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
priv->birth_time = timer_now ();
}
static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
size_t old_size, new_size;
Sess_Private_Data *priv;
Sess *sess;
Time delta, now = timer_now ();
assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
sess = (Sess *) obj;
priv = SESS_PRIVATE_DATA (sess);
delta = (now - priv->birth_time);
if (sess->failed)
{
++st.num_failed;
st.failtime_sum += delta;
}
else
{
st.num_conns += priv->num_conns;
++st.num_completed_since_last_sample;
++st.num_completed;
st.lifetime_sum += delta;
}
if (priv->num_calls_completed > st.longest_session)
{
st.longest_session = priv->num_calls_completed;
if (st.longest_session >= st.len_hist_alloced)
{
old_size = st.len_hist_alloced*sizeof (st.len_hist[0]);
st.len_hist_alloced = st.longest_session + 16;
new_size = st.len_hist_alloced*sizeof (st.len_hist[0]);
st.len_hist = realloc (st.len_hist, new_size);
if (!st.len_hist)
{
fprintf (stderr, "%s.sess_stat: Out of memory\n", prog_name);
exit (1);
}
memset ((char *) st.len_hist + old_size, 0, new_size - old_size);
}
}
++st.len_hist[priv->num_calls_completed];
}
static void
conn_connected (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
Conn *conn;
assert (et == EV_CONN_CONNECTED && object_is_conn (obj));
conn = (Conn *) obj;
sess = session_get_sess_from_conn (conn);
priv = SESS_PRIVATE_DATA (sess);
++priv->num_conns;
}
static void
call_done (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
Sess_Private_Data *priv;
Sess *sess;
Call *call;
assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
call = (Call *) obj;
sess = session_get_sess_from_call (call);
priv = SESS_PRIVATE_DATA (sess);
++priv->num_calls_completed;
}
static void
init (void)
{
Any_Type arg;
size_t size;
sess_private_data_offset = object_expand (OBJ_SESS,
sizeof (Sess_Private_Data));
st.len_hist_alloced = 16;
size = st.len_hist_alloced*sizeof (st.len_hist[0]);
st.len_hist = malloc (size);
memset (st.len_hist, 0, size);
st.rate_min = DBL_MAX;
if (!st.len_hist)
{
fprintf (stderr, "%s.sess_stat: Out of memory\n", prog_name);
exit (1);
}
arg.l = 0;
event_register_handler (EV_PERF_SAMPLE, perf_sample, arg);
event_register_handler (EV_SESS_NEW, sess_created, arg);
event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
event_register_handler (EV_CONN_CONNECTED, conn_connected, arg);
event_register_handler (EV_CALL_RECV_STOP, call_done, arg);
}
static void
dump (void)
{
double min, avg, stddev, delta;
int i;
delta = test_time_stop - test_time_start;
avg = 0;
stddev = 0;
if (delta > 0)
avg = st.num_completed / delta;
if (st.num_rate_samples > 1)
stddev = STDDEV (st.rate_sum, st.rate_sum2, st.num_rate_samples);
if (st.num_rate_samples > 0)
min = st.rate_min;
else
min = 0.0;
printf ("\nSession rate [sess/s]: min %.2f avg %.2f max %.2f "
"stddev %.2f (%u/%u)\n", min, avg, st.rate_max, stddev,
st.num_completed, st.num_completed + st.num_failed);
printf ("Session: avg %.2f connections/session\n",
st.num_completed > 0 ? st.num_conns/(double) st.num_completed : 0.0);
avg = 0.0;
if (st.num_completed > 0)
avg = st.lifetime_sum/st.num_completed;
printf ("Session lifetime [s]: %.1f\n", avg);
avg = 0.0;
if (st.num_failed > 0)
avg = st.failtime_sum/st.num_failed;
printf ("Session failtime [s]: %.1f\n", avg);
printf ("Session length histogram:");
for (i = 0; i <= st.longest_session; ++i)
printf (" %u", st.len_hist[i]);
putchar ('\n');
}
Stat_Collector session_stat =
{
"collects session-related statistics",
init,
no_op,
no_op,
dump
};
07070100000047000081ED0000000000000000000000015FCD000200000718000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/stat/stats.h/*
httperf -- a tool for measuring web server performance
Copyright 2000-2007 Hewlett-Packard Company
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, the copyright holders give
permission to link the code of this work with the OpenSSL project's
"OpenSSL" library (or with modified versions of it that use the same
license as the "OpenSSL" library), and distribute linked combinations
including the two. You must obey the GNU General Public License in
all respects for all of the code used other than "OpenSSL". If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA
*/
#ifndef stats_h
#define stats_h
#include <math.h>
#define SQUARE(f) ((f)*(f))
#define VAR(s,s2,n) (((n) < 2) ? 0.0 : ((s2) - SQUARE(s)/(n)) / ((n) - 1))
#define STDDEV(s,s2,n) (((n) < 2) ? 0.0 : sqrt (VAR ((s), (s2), (n))))
#endif /* stats_h */
07070100000048000081ED0000000000000000000000015FCD0002000019A9000000000000000000000000000000000000002700000000httperf-0.9.0+git.20201206/src/timer.c/*
* Copyright (C) 2007 Ted Bullock <tbullock@comlore.com>
* Copyright (C) 2000 Hewlett-Packard Company
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <generic_types.h>
#include <list.h>
#include <httperf.h>
#define NUM_TIMERS 25
static Time now;
struct Timer {
Time time_started;
Time timeout_delay;
bool has_expired;
/*
* Callback function called when timer expires (timeout)
*/
void (*timeout_callback) (struct Timer * t, Any_Type arg);
/*
* Typically used as a void pointer to the data object being timed
*/
Any_Type timer_subject;
};
/*
* inactive timers
*/
static struct List *passive_timers = NULL;
/*
* active timers
*/
static struct List *active_timers = NULL;
/*
* active timeoutless timers
*/
static struct List *persistent_timers = NULL;
/*
* Returns the time and calls the syscall gettimeofday. This is an expensive
* function since it requires a context switch. Use of the cache is
* preferable with the timer_now function
*/
Time
timer_now_forced(void)
{
struct timeval tv;
gettimeofday(&tv, 0);
return tv.tv_sec + tv.tv_usec * 1e-6;
}
/*
* Returns the current time. If timer caching is enabled then uses the cache.
*/
Time
timer_now(void)
{
if (param.use_timer_cache)
return now;
else
return timer_now_forced();
}
/*
* Initializes a large timer pool cache
* This is a very expensive function. Call before beginning measurements.
* Returns 0 upon a memory allocation error
*/
bool
timer_init(void)
{
passive_timers = list_create();
if (passive_timers == NULL)
goto init_failure;
active_timers = list_create();
if (active_timers == NULL)
goto init_failure;
persistent_timers = list_create();
if (persistent_timers == NULL)
goto init_failure;
for (int i = 0; i < NUM_TIMERS; i++) {
Any_Type a;
a.vp = malloc(sizeof(struct Timer));
if (a.vp == NULL)
goto init_failure;
if (list_push(passive_timers, a) == false)
goto init_failure;
}
now = timer_now_forced();
return true;
init_failure:
fprintf(stderr, "%s.%s: %s\n", __FILE__, __func__, strerror(errno));
return false;
}
/*
* Frees all allocated timers, and timer queues
*/
void
timer_free_all(void)
{
while (!is_list_empty(passive_timers)) {
Any_Type a = list_pop(passive_timers);
free(a.vp);
}
list_free(passive_timers);
passive_timers = NULL;
while (!is_list_empty(active_timers)) {
Any_Type a = list_pop(active_timers);
free(a.vp);
}
list_free(active_timers);
active_timers = NULL;
while (!is_list_empty(persistent_timers)) {
Any_Type a = list_pop(persistent_timers);
free(a.vp);
}
list_free(persistent_timers);
persistent_timers = NULL;
}
/*
* Checks whether a timer has expired
*/
static bool
timer_has_expired(Any_Type a)
{
struct Timer *t = a.vp;
/*
* Only expire currently processing timers
*/
if (t->has_expired == false) {
if (t->time_started + t->timeout_delay < timer_now()) {
t->has_expired = true;
(*t->timeout_callback) (t, t->timer_subject);
return true;
}
}
return false;
}
static bool
timer_deactivate(Any_Type a)
{
struct Timer *t = a.vp;
/* TODO: Error check list_push */
if (t->has_expired == true)
list_push(passive_timers, a);
return t->has_expired;
}
/*
* Checks for timers which have had their timeout value pass and executes their
* callback function. The timer is then removed from the active timer list
* and then enqueued back into the passive timer queue
*/
void
timer_tick(void)
{
now = timer_now_forced();
list_for_each(active_timers, &timer_has_expired);
list_remove_if_true(active_timers, &timer_deactivate);
}
/*
* Schedules a timer into the active_timer list. Usually the timer is
* requisitioned from the passive_timer list to avoid making extra calls
* to malloc, but will allocate memory for a new counter if there are no
* inactive timers available
*/
struct Timer *
timer_schedule(void (*timeout) (struct Timer * t, Any_Type arg),
Any_Type subject, Time delay)
{
struct Timer *t;
if (!is_list_empty(passive_timers)) {
Any_Type a = list_pop(passive_timers);
t = (struct Timer *) a.vp;
} else if ((t = malloc(sizeof(struct Timer))) == NULL)
return NULL;
memset(t, 0, sizeof(struct Timer));
t->timeout_callback = timeout;
t->has_expired = false;
t->timer_subject = subject;
t->time_started = timer_now();
t->timeout_delay = delay;
if (delay > 0 || true)
{
Any_Type temp;
temp.vp = (void *)t;
list_push(active_timers, temp);
}
else
{
Any_Type temp;
temp.vp = (void *)t;
list_push(persistent_timers, temp);
}
if (DBG > 2)
fprintf(stderr,
"timer_schedule: t=%p, delay=%gs, subject=%lx\n", t,
delay, subject.l);
return t;
}
void
timer_cancel(struct Timer *t)
{
if (DBG > 2)
fprintf(stderr, "timer_cancel: t=%p\n", t);
/*
* A module MUST NOT call timer_cancel() for a timer that is currently
* being processed (whose timeout has expired).
*/
t->has_expired = true;
}
07070100000049000081ED0000000000000000000000015FCD00020000080F000000000000000000000000000000000000002700000000httperf-0.9.0+git.20201206/src/timer.h/*
* Copyright (C) 2007 Ted Bullock <tbullock@comlore.com>
* Copyright (C) 2000 Hewlett-Packard Company
*
* This file is part of httperf, a web server performance measurment tool.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef timer_h
#define timer_h
struct Timer;
typedef void (*Timer_Callback) (struct Timer * t, Any_Type arg);
Time timer_now_forced(void);
Time timer_now(void);
bool timer_init(void);
void timer_reset_all(void);
void timer_free_all(void);
/*
* Needs to be called at least once every TIMER_INTERVAL:
*/
void timer_tick(void);
struct Timer *timer_schedule(Timer_Callback timeout, Any_Type arg,
Time delay);
void timer_cancel(struct Timer * t);
#endif /* timer_h */
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!864 blocks