[Zope-dev] Zope Server hanging :-(

Chris McDonough chrism@digicool.com
Fri, 27 Apr 2001 13:02:51 -0400


This is a multi-part message in MIME format.

------=_NextPart_000_01A1_01C0CF1A.5DD65200
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit



> I forgot to mention... our neither our z2.log or stupid_log show anything
> upon freezing.  afterwards the stupid_log shows the failed transaction
> cleanup, but that's it.

Hmmm... it might be useful to turn on detailed request logging (-M logging)
in the start file (see z2.py).

Attached is a script that I just checked into the trunk to do analysis of
the file generated by the -M log.  It can help you figure out if there's a
pattern to the hangs (whether it happens on a particular method, whether it
happens at heavy load time, whether it happens at a particular time of day,
etc.)  Do you think you'd be willing to play around with it a little bit to
try to discover a pattern?



>
> Tim
>
> -----Original Message-----
> From: Tim McLaughlin
> Sent: Friday, April 27, 2001 12:41 PM
> To: 'zope-dev@zope.org'; 'chrisw@nipltd.com'
> Subject: [Zope-dev] Zope Server hanging :-(
>
>
> Chris (and all),
>
> We seem to be having similar crashing issues.  And interestingly enough,
> they don't seem to show up on any of our servers except the 2.3.0 upgraded
> to 2.3.1.
>
> 1.  Hangs on anything from Pythonscript or dtml
> 2.  top shows no significant mem or cpu usage
> 3.  threads are all unresponsive
> 4.  restart usually yields a .trX file (an aborted transaction I suppose)
>
> Any ideas?   Maybe it was something "fixed" in 2.3.1?  The causes seem to
be
> somewhat different except that they all modify the ZODB....
>
> Tim
>
> _______________________________________________
> Zope-Dev maillist  -  Zope-Dev@zope.org
> http://lists.zope.org/mailman/listinfo/zope-dev
> **  No cross posts or HTML encoding!  **
> (Related lists -
>  http://lists.zope.org/mailman/listinfo/zope-announce
>  http://lists.zope.org/mailman/listinfo/zope )
>

------=_NextPart_000_01A1_01C0CF1A.5DD65200
Content-Type: text/plain;
	name="requestprofiler.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="requestprofiler.py"

#!/usr/bin/env python=0A=
#########################################################################=
#####=0A=
# =0A=
# Zope Public License (ZPL) Version 1.0=0A=
# -------------------------------------=0A=
# =0A=
# Copyright (c) Digital Creations.  All rights reserved.=0A=
# =0A=
# This license has been certified as Open Source(tm).=0A=
# =0A=
# Redistribution and use in source and binary forms, with or without=0A=
# modification, are permitted provided that the following conditions are=0A=
# met:=0A=
# =0A=
# 1. Redistributions in source code must retain the above copyright=0A=
#    notice, this list of conditions, and the following disclaimer.=0A=
# =0A=
# 2. Redistributions in binary form must reproduce the above copyright=0A=
#    notice, this list of conditions, and the following disclaimer in=0A=
#    the documentation and/or other materials provided with the=0A=
#    distribution.=0A=
# =0A=
# 3. Digital Creations requests that attribution be given to Zope=0A=
#    in any manner possible. Zope includes a "Powered by Zope"=0A=
#    button that is installed by default. While it is not a license=0A=
#    violation to remove this button, it is requested that the=0A=
#    attribution remain. A significant investment has been put=0A=
#    into Zope, and this effort will continue if the Zope community=0A=
#    continues to grow. This is one way to assure that growth.=0A=
# =0A=
# 4. All advertising materials and documentation mentioning=0A=
#    features derived from or use of this software must display=0A=
#    the following acknowledgement:=0A=
# =0A=
#      "This product includes software developed by Digital Creations=0A=
#      for use in the Z Object Publishing Environment=0A=
#      (http://www.zope.org/)."=0A=
# =0A=
#    In the event that the product being advertised includes an=0A=
#    intact Zope distribution (with copyright and license included)=0A=
#    then this clause is waived.=0A=
# =0A=
# 5. Names associated with Zope or Digital Creations must not be used to=0A=
#    endorse or promote products derived from this software without=0A=
#    prior written permission from Digital Creations.=0A=
# =0A=
# 6. Modified redistributions of any form whatsoever must retain=0A=
#    the following acknowledgment:=0A=
# =0A=
#      "This product includes software developed by Digital Creations=0A=
#      for use in the Z Object Publishing Environment=0A=
#      (http://www.zope.org/)."=0A=
# =0A=
#    Intact (re-)distributions of any official Zope release do not=0A=
#    require an external acknowledgement.=0A=
# =0A=
# 7. Modifications are encouraged but must be packaged separately as=0A=
#    patches to official Zope releases.  Distributions that do not=0A=
#    clearly separate the patches from the original work must be clearly=0A=
#    labeled as unofficial distributions.  Modifications which do not=0A=
#    carry the name Zope may be packaged in any form, as long as they=0A=
#    conform to all of the clauses above.=0A=
# =0A=
# =0A=
# Disclaimer=0A=
# =0A=
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY=0A=
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0A=
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR=0A=
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS=0A=
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,=0A=
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT=0A=
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF=0A=
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND=0A=
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,=0A=
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT=0A=
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=0A=
#   SUCH DAMAGE.=0A=
# =0A=
# =0A=
# This software consists of contributions made by Digital Creations and=0A=
# many individuals on behalf of Digital Creations.  Specific=0A=
# attributions are listed in the accompanying credits file.=0A=
# =0A=
#########################################################################=
#####=0A=
=0A=
""" Request log profiler script """=0A=
=0A=
__version__=3D'$Revision: 1.3 $'[11:-2]=0A=
=0A=
import string, sys, time, getopt, tempfile=0A=
=0A=
class ProfileException(Exception): pass=0A=
=0A=
class Request:=0A=
    def __init__(self):=0A=
        self.url =3D None=0A=
        self.start =3D None=0A=
        self.method =3D None=0A=
        self.t_recdinput =3D None=0A=
        self.isize =3D None=0A=
        self.t_recdoutput =3D None=0A=
        self.osize =3D None=0A=
        self.httpcode =3D None=0A=
        self.t_end =3D None=0A=
        self.elapsed =3D "I"=0A=
        self.active =3D "NA"=0A=
        =0A=
    def put(self, code, t, desc):=0A=
        if code not in ('A', 'B', 'I', 'E'):=0A=
            raise "unknown request code %s" % code=0A=
        if code =3D=3D 'B':=0A=
            self.start =3D t=0A=
            self.method, self.url =3D string.split(string.strip(desc))=0A=
        elif code =3D=3D "I":=0A=
            self.t_recdinput =3D t=0A=
            self.isize =3D string.strip(desc)=0A=
        elif code =3D=3D "A":=0A=
            self.t_recdoutput =3D t=0A=
            self.httpcode, self.osize =3D =
string.split(string.strip(desc))=0A=
        elif code =3D=3D 'E':=0A=
            self.t_end =3D t=0A=
            self.elapsed =3D int(self.t_end - self.start)=0A=
            =0A=
    def isfinished(self):=0A=
        return not self.elapsed =3D=3D "I"=0A=
=0A=
    def prettystart(self):=0A=
        if self.start is not None:=0A=
            t =3D time.localtime(self.start)=0A=
            return time.strftime('%Y-%m-%dT%H:%M:%S', t)=0A=
        else:=0A=
            return "NA"=0A=
        =0A=
    def win(self):=0A=
        if self.t_recdinput is not None and self.start is not None:=0A=
            return self.t_recdinput - self.start=0A=
        else:=0A=
            return "NA"=0A=
        =0A=
    def wout(self):=0A=
        if self.t_recdoutput is not None and self.t_recdinput is not =
None:=0A=
            return self.t_recdoutput - self.t_recdinput=0A=
        else:=0A=
            return "NA"=0A=
=0A=
    def wend(self):=0A=
        if self.t_end is not None and self.t_recdoutput is not None:=0A=
            return self.t_end - self.t_recdoutput=0A=
        else:=0A=
            return "NA"=0A=
=0A=
    def endstage(self):=0A=
        if self.t_end is not None:=0A=
            stage =3D "E"=0A=
        elif self.t_recdoutput is not None:=0A=
            stage =3D "A"=0A=
        elif self.t_recdinput is not None:=0A=
            stage =3D "I"=0A=
        else:=0A=
            stage =3D "B"=0A=
        return stage=0A=
=0A=
    def total(self):=0A=
        stage =3D self.endstage()=0A=
        if stage =3D=3D "B": return 0=0A=
        if stage =3D=3D "I": return self.t_recdinput - self.start=0A=
        if stage =3D=3D "A": return self.t_recdoutput - self.start=0A=
        if stage =3D=3D "E": return self.elapsed=0A=
=0A=
    def prettyisize(self):=0A=
        if self.isize is not None:=0A=
            return self.isize=0A=
        else:=0A=
            return "NA"=0A=
=0A=
    def prettyosize(self):=0A=
        if self.osize is not None:=0A=
            return self.osize=0A=
        else:=0A=
            return "NA"=0A=
=0A=
    def prettyhttpcode(self):=0A=
        if self.httpcode is not None:=0A=
            return self.httpcode=0A=
        else:=0A=
            return "NA"=0A=
=0A=
    def __str__(self):=0A=
        body =3D (=0A=
            self.prettystart(), self.win(), self.wout(), self.wend(),=0A=
            self.total(), self.endstage(), self.prettyosize(),=0A=
            self.prettyhttpcode(), self.active, self.url=0A=
            )=0A=
        return self.fmt % body =0A=
=0A=
    fmt =3D "%19s %4s %4s %4s %3s %1s %7s %4s %4s %s"=0A=
=0A=
    def getheader(self):=0A=
        body =3D ('Start', 'WIn', 'WOut', 'WEnd', 'Tot', 'S', 'OSize',=0A=
                'Code', 'Act', 'URL')=0A=
        return self.fmt % body=0A=
=0A=
=0A=
class Cumulative:=0A=
    def __init__(self, url):=0A=
        self.url =3D url=0A=
        self.times =3D []=0A=
        self.hangs =3D 0=0A=
        self.allelapsed =3D None=0A=
        =0A=
    def put(self, request):=0A=
        elapsed =3D request.elapsed=0A=
        if elapsed =3D=3D "I": self.hangs =3D self.hangs + 1=0A=
        self.times.append(elapsed)=0A=
        =0A=
    def all(self):=0A=
        if self.allelapsed =3D=3D None:=0A=
            self.allelapsed =3D []=0A=
            for elapsed in self.times:=0A=
                self.allelapsed.append(elapsed)=0A=
            self.allelapsed.sort()=0A=
        return self.allelapsed=0A=
=0A=
    def __str__(self):=0A=
        body =3D (=0A=
            self.hangs, self.hits(), self.total(), self.max(), =
self.min(),=0A=
            self.median(), self.mean(), self.url=0A=
            )=0A=
        return self.fmt % body=0A=
=0A=
    def getheader(self):=0A=
        return self.fmt % ('Hangs', 'Hits', 'Total', 'Max', 'Min', =
'Median',=0A=
                           'Mean', 'URL')=0A=
        =0A=
    fmt =3D "%5s %5s %5s %5s %5s %6s %5s %s"=0A=
=0A=
    def hits(self):=0A=
        return len(self.times)=0A=
        =0A=
    def max(self):=0A=
        return max(self.all())=0A=
        =0A=
    def min(self):=0A=
        return min(self.all())=0A=
        =0A=
    def mean(self):=0A=
        l =3D len(self.times)=0A=
        if l =3D=3D 0:=0A=
            return "I"=0A=
        else:=0A=
            t =3D self.total()=0A=
            if t =3D=3D "I": return "I"=0A=
            return t/l=0A=
        =0A=
    def median(self):=0A=
        all =3D self.all()=0A=
        l =3D len(all)=0A=
        if l =3D=3D 0:=0A=
            return "I"=0A=
        else:=0A=
            if l =3D=3D 1:=0A=
                return all[0]=0A=
            elif l % 2 !=3D 0:=0A=
                i =3D l/2 + 1=0A=
                return all[i]=0A=
            else:=0A=
                i =3D l/2 - 1=0A=
                i2 =3D i + 1=0A=
                v1 =3D all[i]=0A=
                v2 =3D all[i2]=0A=
                if v1 =3D=3D "NA" or v2 =3D=3D "NA": return "I"=0A=
                else: return (all[i] + all[i2]) / 2=0A=
    =0A=
    def total(self):=0A=
        t =3D 0=0A=
        all =3D self.all()=0A=
        for elapsed in all:=0A=
            if elapsed =3D=3D "I": continue=0A=
            t =3D t + elapsed=0A=
        return t=0A=
=0A=
def parsebigmlogline(line):=0A=
    tup =3D string.split(line, None, 3)=0A=
    if len(tup) =3D=3D 3:=0A=
        code, id, timestr =3D tup=0A=
        return code, id, timestr, ''=0A=
    elif len(tup) =3D=3D 4:=0A=
        return tup=0A=
    else:=0A=
        return None=0A=
=0A=
def analyze(f, top, sortf, start=3DNone, end=3DNone, =
mode=3D'cumulative'):=0A=
    beginrequests =3D {}=0A=
    cumulative =3D {}=0A=
    finished =3D {}=0A=
    unfinished =3D {}=0A=
    =0A=
    while 1:=0A=
        line =3D f.readline()=0A=
        if not line:=0A=
            break=0A=
        line =3D string.strip(line)=0A=
        tup =3D parsebigmlogline(line)=0A=
        if tup is None:=0A=
            print "Could not interpret line: %s" % line=0A=
            continue=0A=
        code, id, timestr, desc =3D tup=0A=
        timestr =3D string.strip(timestr)=0A=
        fromepoch =3D getdate(timestr)=0A=
        if start is not None and fromepoch < start: continue=0A=
        if end is not None and fromepoch > end: break=0A=
        request =3D unfinished.get(id)=0A=
        if request is None:=0A=
            if code !=3D "B": continue # garbage at beginning of file=0A=
            request =3D Request()=0A=
            unfinished[id] =3D request=0A=
        request.put(code, int(fromepoch), desc)=0A=
        if request.isfinished():=0A=
            del unfinished[id]=0A=
            finished[id] =3D request=0A=
            request.active =3D len(unfinished)=0A=
            =0A=
    finished.update(unfinished)=0A=
    requests =3D finished.values()=0A=
=0A=
    if mode =3D=3D 'cumulative':=0A=
        for request in requests:=0A=
            url =3D request.url=0A=
            stats =3D cumulative.get(url)=0A=
            if stats is None:=0A=
                stats =3D Cumulative(url)=0A=
                cumulative[url] =3D stats=0A=
            stats.put(request)=0A=
=0A=
    cumulative =3D cumulative.values()=0A=
    =0A=
    if mode =3D=3D 'cumulative':=0A=
        dict =3D cumulative=0A=
    elif mode =3D=3D 'detailed':=0A=
        dict =3D requests=0A=
    else:=0A=
        raise "Invalid mode."=0A=
=0A=
    dict.sort(sortf)=0A=
    write(dict, top)=0A=
    =0A=
def write(requests, top=3D0):=0A=
    if len(requests) =3D=3D 0:=0A=
        print "No data.\n"=0A=
        return=0A=
    i =3D 0=0A=
    header =3D requests[0].getheader()=0A=
    print header=0A=
    for stat in requests:=0A=
        i =3D i + 1=0A=
        if verbose:=0A=
            print str(stat)=0A=
        else:=0A=
            print str(stat)[:78]=0A=
        if i =3D=3D top:=0A=
            break=0A=
=0A=
def getdate(val):=0A=
    try:=0A=
        val =3D string.strip(val)=0A=
        year, month, day =3D int(val[:4]), int(val[5:7]), int(val[8:10])=0A=
        =
hour,minute,second=3Dint(val[11:13]),int(val[14:16]),int(val[17:19])=0A=
        t =3D time.mktime((year, month, day, hour, minute, second, 0, 0, =
-1))=0A=
        return t=0A=
    except:=0A=
        raise ProfileException, "bad date %s" % val=0A=
    =0A=
def codesort(v1, v2):=0A=
    v1 =3D v1.endstage()=0A=
    v2 =3D v2.endstage()=0A=
    if v1 =3D=3D v2:=0A=
        return 0=0A=
    =0A=
    if v1 =3D=3D "B":=0A=
        return -1 # v1 is smaller than v2=0A=
    if v1 =3D=3D "I":=0A=
        if v2 =3D=3D "B": return 1 # v1 is larger than v2=0A=
        else: return -1=0A=
    if v1 =3D=3D "A":=0A=
        if v2 in ['B', 'I']: return 1=0A=
        else: return -1=0A=
    if v1 =3D=3D "E":=0A=
        return 1=0A=
=0A=
class Sort:=0A=
    def __init__(self, fname, ascending=3D0):=0A=
        self.fname =3D fname=0A=
        self.ascending =3D ascending=0A=
=0A=
    def __call__(self, i1, i2):=0A=
        f1 =3D getattr(i1, self.fname)=0A=
        f2 =3D getattr(i2, self.fname)=0A=
        if callable(f1): f1 =3D f1()=0A=
        if callable(f2): f2 =3D f2()=0A=
        if f1 < f2:=0A=
            if self.ascending: return -1=0A=
            else: return 1=0A=
        elif f1 =3D=3D f2:=0A=
            return 0=0A=
        else:=0A=
            if self.ascending: return 1=0A=
            else: return -1=0A=
            =0A=
def detailedusage():=0A=
    details =3D usage(0)=0A=
    pname =3D sys.argv[0]=0A=
    details =3D details + """=0A=
Reports are of two types: cumulative or detailed.  The default is =
cumulative.=0A=
Data is taken from the Zope detailed request log (the -M log).=0A=
=0A=
For cumulative reports, each line in the profile indicates information=0A=
about a Zope method (URL) collected via the detailed request log (the -M =
log).=0A=
=0A=
For detailed reports, each line in the profile indicates information =
about=0A=
a single request.=0A=
=0A=
'filename' is the path to the '-M' log that contains detailed request =
data.=0A=
=0A=
If a 'sort' value is specified, sort the profile info by the spec.  The =
sort=0A=
order is descending unless indicated.    The default cumulative sort =
spec is=0A=
'total'.  The default detailed sort spec is 'start'.=0A=
=0A=
For cumulative reports, the following sort specs are accepted:=0A=
=0A=
  'hits'        -- the number of hits against the method=0A=
  'hangs'       -- the number of unfinished requests to the method=0A=
  'max'         -- the maximum time in secs taken by a request to this =
method=0A=
  'min'         -- the minimum time in secs taken by a request to this =
method=0A=
  'mean'        -- the mean time in secs taken by a request to this =
method=0A=
  'median'      -- the median time in secs taken by a request to this =
method=0A=
  'total'       -- the total time in secs across all requests to this =
method=0A=
  'url'         -- the URL/method name (ascending)=0A=
=0A=
For detailed (non-cumulative) reports, the following sort specs are =
accepted:=0A=
=0A=
  'start'       -- the start time of the request to ZServer (ascending)=0A=
  'win'         -- the num of secs ZServer spent waiting for input from =
client=0A=
  'wout'        -- the secs ZServer spent waiting for output from =
ZPublisher=0A=
  'wend'        -- the secs ZServer spent sending data to the client=0A=
  'total'       -- the secs taken for the request from begin to end=0A=
  'endstage'    -- the last successfully completed request stage (B, I, =
A, E)=0A=
  'osize'       -- the size in bytes of output provided by ZPublisher=0A=
  'httpcode'    -- the HTTP response code provided by ZPublisher =
(ascending)=0A=
  'active'      -- total num of requests pending at the end of this =
request=0A=
  'url'         -- the URL/method name (ascending)=0A=
=0A=
  NOTE:  'active' count may be fooled by Zope restarts, which aren't=0A=
  reflected in the -M log.=0A=
=0A=
If the 'top' argument is specified, only report on the top 'n' entries in=0A=
the profile (as per the sort). The default is to show all data in the =
profile.=0A=
=0A=
If the 'verbose' argument is specified, do not trim url to fit into 80 =
cols.=0A=
=0A=
If the 'today' argument is specified, limit results to hits received =
today.=0A=
=0A=
If the 'start' argument is specified in the form 'DD/MM/YYYY HH:MM:SS' =
(UTC),=0A=
limit results to hits received after this date/time.=0A=
=0A=
If the 'end' argument is specified in the form 'DD/MM/YYYY HH:MM:SS' =
(UTC),=0A=
limit results to hits received before this date/time.=0A=
=0A=
Examples:=0A=
=0A=
  %(pname)s debug.log=0A=
=0A=
    Show cumulative report statistics for information in the file =
'debug.log',=0A=
    by default sorted by 'total'.=0A=
=0A=
  %(pname)s debug.log --detailed=0A=
=0A=
    Show detailed report statistics sorted by 'start' (by default).=0A=
=0A=
  %(pname)s debug.log --cumulative --sort=3Dmean --today --verbose=0A=
=0A=
    Show cumulative report statistics sorted by mean for entries in the =
log=0A=
    which happened today, and do not trim the URL in the resulting =
report.=0A=
=0A=
  %(pname)s debug.log --detailed --start=3D'2001/05/10 06:00:00'=0A=
    --end=3D'2001/05/11 23:00:00'=0A=
=0A=
    Show detailed report statistics for entries in 'debug.log' which=0A=
    begin after 6am UTC on May 10, 2001 and which end before=0A=
    11pm UTC on May 11, 2001.=0A=
=0A=
  %(pname)s debug.log --top=3D100 --sort=3Dmax=0A=
=0A=
    Show cumulative report of the the 'top' 100 methods sorted by maximum=0A=
    elapsed time.""" % {'pname':pname}=0A=
    return details=0A=
=0A=
def usage(basic=3D1):=0A=
    usage =3D (=0A=
        """=0A=
Usage: %s filename [--cumulative|--detailed]=0A=
                   [--sort=3Dspec]=0A=
                   [--top=3D=3Dn]=0A=
                   [--verbose]=0A=
                   [ --today | [--start=3Ddate] [--end=3Ddate] ]=0A=
                   [--help]=0A=
        =0A=
Provides a profile of the detailed (-M) Zope request log.=0A=
""" % sys.argv[0]=0A=
        )=0A=
    if basic =3D=3D 1:=0A=
        usage =3D usage + """=0A=
If the --help argument is given, detailed usage docs are provided."""=0A=
    return usage=0A=
=0A=
=0A=
if __name__ =3D=3D '__main__':=0A=
    if len(sys.argv) =3D=3D 1:=0A=
        print usage()=0A=
        sys.exit(0)=0A=
    if sys.argv[1] =3D=3D '--help': print detailedusage(); sys.exit(0)=0A=
    mode =3D 'cumulative'=0A=
    sortby =3D None=0A=
    trim =3D 0=0A=
    top =3D 0=0A=
    verbose =3D 0=0A=
    start =3D None=0A=
    end =3D None=0A=
    try:=0A=
        opts, extra =3D getopt.getopt(=0A=
            sys.argv[2:], '', ['sort=3D', 'top=3D', 'help', 'verbose', =
'today',=0A=
                               'cumulative', 'detailed', 'start=3D',=0A=
                               'end=3D']=0A=
            )=0A=
        for opt, val in opts:=0A=
            if opt=3D=3D'--sort': sortby =3D val=0A=
            if opt=3D=3D'--top': top=3Dint(val)=0A=
            if opt=3D=3D'--help': print detailedusage(); sys.exit(0)=0A=
            if opt=3D=3D'--verbose':=0A=
                global verbose=0A=
                verbose =3D 1=0A=
            if opt=3D=3D'--today':=0A=
                now =3D time.gmtime(time.time())=0A=
                # for testing - now =3D (2001, 04, 19, 0, 0, 0, 0, 0, -1)=0A=
                start =3D list(now)=0A=
                start[3] =3D start[4] =3D start[5] =3D 0=0A=
                start =3D time.mktime(start)=0A=
                end =3D list(now)=0A=
                end[3] =3D 23; end[4] =3D 59; end[5] =3D 59=0A=
                end =3D time.mktime(end)=0A=
            if opt=3D=3D'--start':=0A=
                start =3D getdate(val)=0A=
            if opt=3D=3D'--end':=0A=
                end =3D getdate(val)=0A=
            if opt=3D=3D'--detailed':=0A=
                mode=3D'detailed'=0A=
                d_sortby =3D sortby=0A=
            if opt=3D=3D'--cumulative':=0A=
                mode=3D'cumulative'=0A=
=0A=
        validcumsorts =3D ['url', 'hits', 'hangs', 'max', 'min', =
'median',=0A=
                         'mean', 'total']=0A=
        validdetsorts =3D ['start', 'win', 'wout', 'wend', 'total',=0A=
                         'endstage', 'isize', 'osize', 'httpcode',=0A=
                         'active', 'url']=0A=
=0A=
        if mode =3D=3D 'cumulative':=0A=
            if sortby is None: sortby =3D 'total'=0A=
            assert sortby in validcumsorts, (sortby, mode, validcumsorts)=0A=
            if sortby in ['url']:=0A=
                sortf =3D Sort(sortby, ascending=3D1)=0A=
            else:=0A=
                sortf =3D Sort(sortby)=0A=
        elif mode =3D=3D 'detailed':=0A=
            if sortby is None: sortby =3D 'start'=0A=
            assert sortby in validdetsorts, (sortby, mode, validdetsorts)=0A=
            if sortby in ['start', 'url', 'httpcode']:=0A=
                sortf =3D Sort(sortby, ascending=3D1)=0A=
            elif sortby =3D=3D 'endstage':=0A=
                sortf =3D codesort=0A=
            else:=0A=
                sortf =3D Sort(sortby)=0A=
        else:=0A=
            raise 'Invalid mode'=0A=
        =0A=
        analyze(open(sys.argv[1]), top, sortf, start, end, mode)=0A=
=0A=
    except AssertionError, val:=0A=
        a =3D "%s is not a valid %s sort spec, use one of %s"=0A=
        print a % (val[0], val[1], val[2])=0A=
        sys.exit(0)=0A=
    except getopt.error, val:=0A=
        print val=0A=
        sys.exit(0)=0A=
    except ProfileException, val:=0A=
        print val=0A=
        sys.exit(0)=0A=
    except SystemExit:=0A=
        sys.exit(0)=0A=
    except:=0A=
        import traceback=0A=
        traceback.print_exc()=0A=
        print usage()=0A=
        sys.exit(0)=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=

------=_NextPart_000_01A1_01C0CF1A.5DD65200--