#!/usr/bin/env python
import sys, re, struct, mmap, time
from bup import git, options
from bup.helpers import *

handle_ctrl_c()

def s_from_bytes(bytes):
    clist = [chr(b) for b in bytes]
    return ''.join(clist)


last = start = 0
def report(count):
    global last, start
    fields = ['VmSize', 'VmRSS', 'VmData', 'VmStk', 'ms']
    d = {}
    for line in open('/proc/self/status').readlines():
        l = re.split(r':\s*', line.strip(), 1)
        d[l[0]] = l[1]
    now = time.time()
    d['ms'] = int((now - last) * 1000)
    if count >= 0:
        e1 = count
        fields = [d[k] for k in fields]
    else:
        e1 = ''
        start = now
    print ('%9s  ' + ('%10s ' * len(fields))) % tuple([e1] + fields)
    sys.stdout.flush()
    last = time.time()


optspec = """
bup memtest [-n elements] [-c cycles]
--
n,number=  number of objects per cycle [10000]
c,cycles=  number of cycles to run [100]
ignore-midx  ignore .midx files, use only .idx files
existing   test with existing objects instead of fake ones
"""
o = options.Options('bup memtest', optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])

if extra:
    o.fatal('no arguments expected')

git.ignore_midx = opt.ignore_midx

git.check_repo_or_die()
m = git.PackIdxList(git.repo('objects/pack'))

report(-1)
f = open('/dev/urandom')
a = mmap.mmap(-1, 20)
report(0)

if opt.existing:
    def foreverit(mi):
        while 1:
            for e in mi:
                yield e
    objit = iter(foreverit(m))
    
for c in xrange(opt.cycles):
    for n in xrange(opt.number):
        if opt.existing:
            bin = objit.next()
            assert(m.exists(bin))
        else:
            b = f.read(3)
            a[0:2] = b[0:2]
            a[2] = chr(ord(b[2]) & 0xf0)
            bin = str(a[0:20])

            # technically, a randomly generated object id might exist.
            # but the likelihood of that is the likelihood of finding
            # a collision in sha-1 by accident, which is so unlikely that
            # we don't care.
            assert(not m.exists(bin))
    report((c+1)*opt.number)

print ('%d objects searched in %d steps: avg %.3f steps/object' 
       % (git._total_searches, git._total_steps,
          git._total_steps*1.0/git._total_searches))
print 'Total time: %.3fs' % (time.time() - start)

@@ -49,16 +49,26 @@
 ap, time
+, resource
 %0Afrom bu
@@ -226,41 +226,44 @@
 )%0A%0A%0A
-last = start = 0%0Adef report(count
+_linux_warned = 0%0Adef linux_memstat(
 ):%0A 
@@ -276,24 +276,27 @@
 bal 
-last, start
+_linux_warned
 %0A    
+#
 fiel
@@ -364,19 +364,24 @@
     
-for line in
+try:%0A        f =
  ope
@@ -406,20 +406,164 @@
 us')
-.readlines()
+%0A    except IOError, e:%0A        if not _linux_warned:%0A            log('Warning: %25s%5Cn' %25 e)%0A            _linux_warned = 1%0A        return %7B%7D%0A    for line in f
 :%0A  
@@ -568,17 +568,19 @@
         
-l
+k,v
  = re.sp
@@ -623,251 +623,613 @@
   d%5B
-l%5B0%5D%5D = l%5B1%5D%0A    now = time.time()%0A    d%5B'ms'%5D = int((now - last) * 1000)%0A    if count %3E= 0:%0A        e1 = count%0A        fields = %5Bd%5Bk%5D for k in fields%5D%0A    else:%0A        e1 = ''%0A        start = now%0A    print ('%259s  ' + ('%2510s ' * len(fields)))
+k%5D = v%0A    return d%0A%0A%0Alast = last_u = last_s = start = 0%0Adef report(count):%0A    global last, last_u, last_s, start%0A    headers = %5B'RSS', 'MajFlt', 'user', 'sys', 'ms'%5D%0A    ru = resource.getrusage(resource.RUSAGE_SELF)%0A    now = time.time()%0A    rss = int(ru.ru_maxrss/1024)%0A    if not rss:%0A        rss = linux_memstat().get('VmRSS', '??')%0A    fields = %5Brss,%0A              ru.ru_majflt,%0A              int((ru.ru_utime - last_u) * 1000),%0A              int((ru.ru_stime - last_s) * 1000),%0A              int((now - last) * 1000)%5D%0A    fmt = '%259s  ' + ('%2510s ' * len(fields))%0A    if count %3E= 0:%0A        print fmt
  %25 t
@@ -1238,10 +1238,13 @@
 le(%5B
-e1
+count
 %5D + 
@@ -1259,26 +1259,260 @@
     
-sys.stdout.flush()
+else:%0A        start = now%0A        print fmt %25 tuple(%5B''%5D + headers)%0A    sys.stdout.flush()%0A    %0A    # don't include time to run report() in usage counts%0A    ru = resource.getrusage(resource.RUSAGE_SELF)%0A    last_u = ru.ru_utime%0A    last_s = ru.ru_stime
 %0A   

use getrusage() instead of /proc/self/stat.