Permalink
Browse files

Switch from 'mmstats-' to '.mmstats' for default file naming

Also clean up a lot of the varied ways of discovering mmstats files by
trying to use mmstats.defaults.DEFAULT_{GLOB,PATH} everywhere.
  • Loading branch information...
1 parent cdfb76d commit 7232353fa6895d7b025a72571e29cced8f7bf9b9 @schmichael committed Sep 26, 2012
Showing with 57 additions and 34 deletions.
  1. +15 −5 mmstats/_mmap.py
  2. +3 −1 mmstats/defaults.py
  3. +6 −2 mmstats/libgettid.py
  4. +4 −7 mmstats/mmash.py
  5. +2 −2 mmstats/mmash_settings.py
  6. +14 −4 mmstats/models.py
  7. +9 −6 mmstats/pollstats.py
  8. +4 −7 mmstats/slurpstats.py
View
@@ -4,6 +4,7 @@
libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
import mmap as stdlib_mmap
import os
+import sys
from . import libgettid
@@ -16,12 +17,21 @@
def init_mmap(path=DEFAULT_PATH, filename=DEFAULT_FILENAME, size=PAGESIZE):
"""Given path, filename => filename, size, mmap
- In `filename` "%PID%" and "%TID%" will be replaced with pid and thread id
+ :param path: path to store mmaped files
+ :param filename: filename template for mmaped files
+ :param size: minimum size of the mmaped region (will be rounded up to the
+ nearest multiple of `PAGESIZE`)
+
+ Substitutions documented in :class:`~mmstats.models.BaseMmStats`
"""
- # Replace %PID% with actual pid
- filename = filename.replace('%PID%', str(os.getpid()))
- # Replace %TID% with thread id or 0 if thread id is None
- filename = filename.replace('%TID%', str(libgettid.gettid() or 0))
+ substitutions = {
+ 'CMD': os.path.basename(sys.argv[0]),
+ 'PID': os.getpid(),
+ 'TID': libgettid.gettid(),
+ }
+ # Format filename and path with substitution variables
+ filename = filename.format(**substitutions)
+ path = path.format(**substitutions)
full_path = os.path.join(path, filename)
View
@@ -7,5 +7,7 @@
SIZE_TYPE = ctypes.c_ushort
WRITE_BUFFER_UNUSED = 255
DEFAULT_PATH = os.getenv('MMSTATS_PATH', tempfile.gettempdir())
-DEFAULT_FILENAME = os.getenv('MMSTATS_FILES', 'mmstats-%PID%-%TID%')
+DEFAULT_FILENAME = os.getenv('MMSTATS_FILES', '{CMD}-{PID}-{TID}.mmstats')
+DEFAULT_GLOB = os.getenv(
+ 'MMSTATS_GLOB', os.path.join(DEFAULT_PATH, '*.mmstats'))
DEFAULT_STRING_SIZE = 255
@@ -10,8 +10,12 @@ def _linux_gettid():
def _universal_gettid():
- """Give up and just use Python's threading ident"""
- return threading.current_thread().ident
+ """Give up and just use Python's threading ident
+
+ Some platforms erroneously return None for the main thread's ident. This
+ will return 0 instead.
+ """
+ return threading.current_thread().ident or 0
if 'linux' in sys.platform:
View
@@ -16,12 +16,10 @@
if 'MMASH_SETTINGS' in os.environ:
app.config.from_envvar('MMASH_SETTINGS')
-GLOB = os.path.join(app.config['MMSTATS_DIR'], 'mmstats-*')
-
def iter_stats():
- """Yields a label at a time from every mmstats file in MMSTATS_DIR"""
- for fn in glob.glob(GLOB):
+ """Yields a label at a time from every mmstats file in MMSTATS_GLOB"""
+ for fn in glob.glob(app.config['MMSTATS_GLOB']):
try:
for label, value in mmstats_reader.MmStatsReader.from_mmap(fn):
yield fn, label, value
@@ -59,12 +57,11 @@ def graph():
else:
numeric_stats.append(stat_data)
return flask.render_template('graph.html',
- mmstats_dir=app.config['MMSTATS_DIR'],
+ mmstats_dir=app.config['MMSTATS_GLOB'],
string_stats=sorted(string_stats, key=lambda x: x['label']),
numeric_stats=sorted(numeric_stats, key=lambda x: x['label']))
-
aggregators = {
'avg': lambda v: float(sum(v)) / len(v),
'one': operator.itemgetter(0),
@@ -94,7 +91,7 @@ def getstat(statname):
@app.route('/')
def index():
return flask.render_template('index.html',
- mmstats_dir=app.config['MMSTATS_DIR'],
+ mmstats_dir=app.config['MMSTATS_GLOB'],
stats=sorted(find_labels()))
@@ -1,6 +1,6 @@
-import tempfile
+from mmstats import defaults
-MMSTATS_DIR = tempfile.gettempdir()
+MMSTATS_GLOB = defaults.DEFAULT_GLOB
DEBUG = True
HOST = '0.0.0.0'
PORT = 5002
View
@@ -16,13 +16,23 @@ def __init__(self, field):
class BaseMmStats(threading.local):
- """Stats models should inherit from this"""
+ """Stats models should inherit from this
+
+ Optionally given a filename or label_prefix, create an MmStats instance
+
+ Both `filename` and `path` support the following variable substiutions:
+
+ * `{CMD}` - name of application (`os.path.basename(sys.argv[0])`)
+ * `{PID}` - process's PID (`os.getpid()`)
+ * `{TID}` - thread ID (tries to get it via the `SYS_gettid` syscall but
+ fallsback to the Python/pthread ID or 0 for truly broken platforms)
+
+ This class is *not threadsafe*, so you should include both {PID} and
+ {TID} in your filename to ensure the mmaped files don't collide.
+ """
def __init__(self, path=DEFAULT_PATH, filename=DEFAULT_FILENAME,
label_prefix=None):
- """\
- Optionally given a filename or label_prefix, create an MmStats instance
- """
self._removed = False
# Setup label prefix
View
@@ -73,7 +73,6 @@
}
-
opts = argparse.ArgumentParser()
opts.add_argument('-c', '--count', type=int)
opts.add_argument('-d', '--delay', type=int, default=1)
@@ -86,7 +85,7 @@
opts.add_argument('-v', action='append_const', const=1, dest='verbosity',
default=[], help='verbosity: warnings=1, info=2, debug=3')
opts.add_argument('--version', action='version',
- version='%%(prog)s %s' % VERSION)
+ version='%%(prog)s %s' % VERSION)
def get_console_size():
@@ -148,8 +147,13 @@ def __init__(self, args):
def _mmap_files(self):
for fn in set(self.args.files):
- f = open(fn, 'rb')
- m = mmap.mmap(f.fileno(), 0, prot=mmap.ACCESS_READ)
+ try:
+ f = open(fn, 'rb')
+ m = mmap.mmap(f.fileno(), 0, prot=mmap.ACCESS_READ)
+ except Exception:
+ self.warn('Skipping %s - unable to open' % fn)
+ continue
+
if m.read_byte() == reader.VERSION_1:
self.files[fn] = Mmap(f, m)
else:
@@ -205,7 +209,7 @@ def print_headers(self):
#field_width = (width / len(self.fields)) - 1
width = 20
print '|'.join(
- ansi['bold'] +
+ ansi['bold'] +
f.replace(self.prefix, '', 1)[:width].center(width)
+ ansi['default']
for f in self.fields
@@ -239,7 +243,6 @@ def run(self):
time.sleep(self.args.delay)
-
def main():
"""CLI Entry Point"""
p = PollStats(opts.parse_args())
View
@@ -1,11 +1,10 @@
#!/usr/bin/env python
+import glob
import mmap
-import os
import sys
-import tempfile
import traceback
-from mmstats import reader as mmstats_reader
+from mmstats import defaults, reader as mmstats_reader
def err(*args):
@@ -41,14 +40,12 @@ def main():
# Only read from tempdir if no files specified on the command line
if not stats_files:
- tempdir = tempfile.gettempdir()
- stats_files = (os.path.join(tempdir, fn) for fn in os.listdir(tempdir)
- if fn.startswith('mmstats-'))
+ stats_files = glob.glob(defaults.DEFAULT_GLOB)
for fn in stats_files:
with open(fn) as f:
- mmst = mmap.mmap(f.fileno(), 0, prot=mmap.ACCESS_READ)
try:
+ mmst = mmap.mmap(f.fileno(), 0, prot=mmap.ACCESS_READ)
slurp_stats(fn, mmst)
except Exception:
err('Error reading: %s' % fn)

0 comments on commit 7232353

Please sign in to comment.