sapling/eden/cli/stats.py

218 lines
7.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
#
# Copyright (c) 2004-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
import argparse
import sys
from . import cmd_util
from . import stats_print
# Function that shows memory related informtion like memory usage, free memory
# etc
def do_stat_memory(args: argparse.Namespace):
out = sys.stdout
stats_print.write_heading('Memory Stat for EdenFs', out)
config = cmd_util.create_config(args)
with config.get_thrift_client() as client:
diag_info = client.getStatInfo()
stats_print.write_mem_status_table(diag_info.counters, out)
# print memory counters
heading = 'Average values of Memory usage and availability'
out.write('\n\n %s \n\n' % heading.center(80, ' '))
mem_counters = get_memory_counters(diag_info.counters)
stats_print.write_table(mem_counters, '', out)
# Gets Information like memory usage, List of mount points and number of Inodes
# loaded, unloaded and materialized in the mount points etc.
def do_stat_general(args: argparse.Namespace):
out = sys.stdout
stats_print.write_heading('General Stat Information for EdenFs', out)
config = cmd_util.create_config(args)
with config.get_thrift_client() as client:
diag_info = client.getStatInfo()
stats_print.write_mem_status_table(diag_info.counters, out)
format_str = '{:>40} {:^1} {:<20}\n'
# Print Inodes unloaded by unload Job.
out.write(
format_str.format(
'Inodes unloaded by Periodic Job', ':', '%d\n' %
diag_info.periodicUnloadCount
)
)
# print InodeInfo for all the mountPoints
inode_info = diag_info.mountPointInfo
for key in inode_info:
out.write('MountPoint Information for %s\n' % key)
out.write(
'Loaded Inodes in memory : %d\n' %
inode_info[key].loadedInodeCount
)
out.write(
'Unloaded Inodes in memory : %d\n' %
inode_info[key].unloadedInodeCount
)
out.write(
'Materialized Inodes in memory : %d\n\n' %
inode_info[key].materializedInodeCount
)
# Prints information about Number of times a system call is performed in EdenFs.
def do_stat_fuse_counters(args: argparse.Namespace):
out = sys.stdout
stats_print.write_heading(
'Counts of I/O operations performed in EdenFs', out
)
config = cmd_util.create_config(args)
with config.get_thrift_client() as client:
diag_info = client.getStatInfo()
# If the arguments has --all flag, we will have args.all set to true.
fuse_counters = get_fuse_counters(diag_info.counters, args.all)
stats_print.write_table(fuse_counters, 'SystemCall', out)
# Prints the Latencies of system calls in EdenFs.
def do_stat_latency(args: argparse.Namespace):
out = sys.stdout
config = cmd_util.create_config(args)
with config.get_thrift_client() as client:
diag_info = client.getStatInfo()
table = get_fuse_latency(diag_info.counters, args.all)
stats_print.write_heading(
'Latencies of I/O operations performed in EdenFs', out
)
stats_print.write_latency_table(table, out)
# Filters Fuse counters from all the counters in ServiceData and returns a
# printable form of the information in a table. If all_flg is true we get the
# counters for all the system calls, otherwise we get the counters of the
# system calls which are present in the list syscalls, which is a list of
# frequently called io system calls.
def get_fuse_counters(counters, all_flg: bool):
table = {}
index = {'60': 0, '600': 1, '3600': 2}
# list of io system calls, if all flag is set we return counters for all the
# systems calls, else we return counters for io systemcalls.
syscalls = [
'open', 'read', 'write', 'symlink', 'readlink', 'mkdir', 'mknod',
'opendir', 'readdir', 'rmdir'
]
for key in counters:
if key.startswith('fuse') and key.find('.count') >= 0:
tokens = key.split('.')
syscall = tokens[1][:-3] # _us
if not all_flg and syscall not in syscalls:
continue
if syscall not in table.keys():
table[syscall] = [0, 0, 0, 0]
if len(tokens) == 3:
table[syscall][3] = str(counters[key])
else:
table[syscall][index[tokens[3]]] = str(counters[key])
return table
# Returns all the memory counters in ServiceData in a table format.
def get_memory_counters(counters):
table = {}
index = {'60': 0, '600': 1, '3600': 2}
for key in counters:
if key.startswith('memory') and key.find('.') != -1:
tokens = key.split('.')
memKey = tokens[0].replace('_', ' ')
if memKey not in table.keys():
table[memKey] = [0, 0, 0, 0]
if len(tokens) == 2:
table[memKey][3] = counters[key]
else:
table[memKey][index[tokens[2]]] = counters[key]
return table
# Returns all the latency information in ServiceData in a table format.
# If all_flg is true we get the counters for all the system calls, otherwise we
# get the counters of the system calls which are present in the list syscalls,
# which is a list of frequently called io system calls.
def get_fuse_latency(counters, all_flg: bool):
table = {}
index = {'60': 0, '600': 1, '3600': 2}
percentile = {'p50': 0, 'p90': 1, 'p99': 2}
syscalls = [
'open', 'read', 'write', 'symlink', 'readlink', 'mkdir', 'mknod',
'opendir', 'readdir', 'rmdir'
]
def with_microsecond_units(i):
if i:
return str(i) + u" \u03BCs" # mu for micro
else:
return str(i) + ' '
for key in counters:
if key.startswith('fuse') and key.find('.count') == -1:
tokens = key.split('.')
syscall = tokens[1][:-3]
if not all_flg and syscall not in syscalls:
continue
if syscall not in table.keys():
table[syscall] = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
i = percentile[tokens[2]]
j = 3
if len(tokens) > 3:
j = index[tokens[3]]
table[syscall][i][j] = with_microsecond_units(counters[key])
return table
def setup_argparse(parser: argparse.ArgumentParser):
subparsers = parser.add_subparsers(dest='subparser_name')
parser = subparsers.add_parser(
'memory', help='Shows memory status of Edenfs'
)
parser.set_defaults(func=do_stat_memory)
parser = subparsers.add_parser(
'io',
help='Shows status about number calls made for each io systemcall'
)
parser.add_argument(
'-A',
'--all',
action='store_true',
default=False,
help='Show status for all the system calls'
)
parser.set_defaults(func=do_stat_fuse_counters)
parser = subparsers.add_parser(
'latency', help='Shows latency report for io systemcalls'
)
parser.add_argument(
'-A',
'--all',
action='store_true',
default=False,
help='Show status for all the system calls'
)
parser.set_defaults(func=do_stat_latency)