]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
tools/rgw: add script to inspect admin socket "cr dump" 15554/head
authorCasey Bodley <cbodley@redhat.com>
Wed, 7 Jun 2017 13:39:42 +0000 (09:39 -0400)
committerCasey Bodley <cbodley@redhat.com>
Wed, 7 Jun 2017 13:39:42 +0000 (09:39 -0400)
Signed-off-by: Casey Bodley <cbodley@redhat.com>
src/tools/rgw/parse-cr-dump.py [new file with mode: 0755]

diff --git a/src/tools/rgw/parse-cr-dump.py b/src/tools/rgw/parse-cr-dump.py
new file mode 100755 (executable)
index 0000000..539929b
--- /dev/null
@@ -0,0 +1,168 @@
+#!/usr/bin/python
+from __future__ import print_function
+from collections import Counter
+import argparse
+import json
+import re
+import sys
+
+def gen_mgrs(args, cr_dump):
+    """ traverse and return one manager at a time """
+    mgrs = cr_dump['coroutine_managers']
+    if args.manager is not None:
+        yield mgrs[args.manager]
+    else:
+        for mgr in mgrs:
+            yield mgr
+
+def gen_stacks(args, cr_dump):
+    """ traverse and return one stack at a time """
+    for mgr in gen_mgrs(args, cr_dump):
+        for ctx in mgr['run_contexts']:
+            for stack in ctx['entries']:
+                yield stack
+
+def gen_ops(args, cr_dump):
+    """ traverse and return one op at a time """
+    for stack in gen_stacks(args, cr_dump):
+        for op in stack['ops']:
+            yield stack, op
+
+def op_status(op):
+    """ return op status or (none) """
+    # "status": {"status": "...", "timestamp": "..."}
+    return op.get('status', {}).get('status', '(none)')
+
+def do_crs(args, cr_dump):
+    """ print a sorted list of coroutines """
+    counter = Counter()
+
+    if args.group == 'status':
+        print('Count:\tStatus:')
+        for _, op in gen_ops(args, cr_dump):
+            if args.filter and not re.search(args.filter, op['type']):
+                continue
+            counter[op_status(op)] += 1
+    else:
+        print('Count:\tCoroutine:')
+        for _, op in gen_ops(args, cr_dump):
+            name = op['type']
+            if args.filter and not re.search(args.filter, name):
+                continue
+            counter[name] += 1
+
+    crs = counter.most_common();
+
+    if args.order == 'asc':
+        crs.reverse()
+    if args.limit:
+        crs = crs[:args.limit]
+
+    for op in crs:
+        print('%d\t%s' % (op[1], op[0]))
+    print('Total:', sum(counter.values()))
+    return 0
+
+def match_ops(name, ops):
+    """ return true if any op matches the given filter """
+    for op in ops:
+        if re.search(name, op):
+            return True
+    return False
+
+def do_stacks(args, cr_dump):
+    """ print a list of coroutine stacks """
+    print('Stack:\t\tCoroutines:')
+    count = 0
+    for stack in gen_stacks(args, cr_dump):
+        stack_id = stack['stack']
+        ops = [op['type'] for op in stack['ops']]
+        if args.filter and not match_ops(args.filter, ops):
+            continue
+        if args.limit and count == args.limit:
+            print('...')
+            break
+        print('%s\t%s' % (stack_id, ', '.join(ops)))
+        count += 1
+    print('Total:', count)
+    return 0
+
+def traverse_spawned_stacks(args, stack, depth, stacks, callback):
+    """ recurse through spawned stacks, passing each op to the callback """
+    for op in stack['ops']:
+        # only filter ops in base stack
+        if depth == 0 and args.filter and not re.search(args.filter, op['type']):
+            continue
+        if not callback(stack, op, depth):
+            return False
+        for spawned in op.get('spawned', []):
+            s = stacks.get(spawned)
+            if not s:
+                continue
+            if not traverse_spawned_stacks(args, s, depth + 1, stacks, callback):
+                return False
+    return True
+
+def do_stack(args, cr_dump):
+    """ inspect a given stack and its descendents """
+    # build a lookup table of stacks by id
+    stacks = {s['stack']: s for s in gen_stacks(args, cr_dump)}
+
+    stack = stacks.get(args.stack)
+    if not stack:
+        print('Stack %s not found' % args.stack, file=sys.stderr)
+        return 1
+
+    do_stack.count = 0 # for use in closure
+    def print_stack_op(stack, op, depth):
+        indent = ' ' * depth * 4
+        if args.limit and do_stack.count == args.limit:
+            print('%s...' % indent)
+            return False # stop traversal
+        do_stack.count += 1
+        print('%s[%s] %s: %s' % (indent, stack['stack'], op['type'], op_status(op)))
+        return True
+
+    traverse_spawned_stacks(args, stack, 0, stacks, print_stack_op)
+    return 0
+
+def do_spawned(args, cr_dump):
+    """ search all ops for the given spawned stack """
+    for stack, op in gen_ops(args, cr_dump):
+        if args.stack in op.get('spawned', []):
+            print('Stack %s spawned by [%s] %s' % (args.stack, stack['stack'], op['type']))
+            return 0
+    print('Stack %s not spawned' % args.stack, file=sys.stderr)
+    return 1
+
+def main():
+    parser = argparse.ArgumentParser(description='Parse and inspect the output of the "cr dump" admin socket command.')
+    parser.add_argument('--filename', type=argparse.FileType(), default=sys.stdin, help='Input filename (or stdin if empty)')
+    parser.add_argument('--filter', type=str, help='Filter by coroutine type (regex syntax is supported)')
+    parser.add_argument('--limit', type=int)
+    parser.add_argument('--manager', type=int, help='Index into coroutine_managers[]')
+
+    subparsers = parser.add_subparsers()
+
+    crs_parser = subparsers.add_parser('crs', help='Produce a sorted list of coroutines')
+    crs_parser.add_argument('--group', type=str, choices=['type', 'status'])
+    crs_parser.add_argument('--order', type=str, choices=['desc', 'asc'])
+    crs_parser.set_defaults(func=do_crs)
+
+    stacks_parser = subparsers.add_parser('stacks', help='Produce a list of coroutine stacks and their ops')
+    stacks_parser.set_defaults(func=do_stacks)
+
+    stack_parser = subparsers.add_parser('stack', help='Inspect a given coroutine stack')
+    stack_parser.add_argument('stack', type=str)
+    stack_parser.set_defaults(func=do_stack)
+
+    spawned_parser = subparsers.add_parser('spawned', help='Find the op that spawned the given stack')
+    spawned_parser.add_argument('stack', type=str)
+    spawned_parser.set_defaults(func=do_spawned)
+
+    args = parser.parse_args()
+    return args.func(args, json.load(args.filename))
+
+if __name__ == "__main__":
+    result = main()
+    sys.exit(result)