2017-08-25 22:41:41 +03:00
|
|
|
#!/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('.')
|
2017-10-21 00:00:28 +03:00
|
|
|
syscall = tokens[1][:-3] # _us
|
2017-08-25 22:41:41 +03:00
|
|
|
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:
|
2017-10-21 00:00:28 +03:00
|
|
|
table[syscall][3] = str(counters[key])
|
2017-08-25 22:41:41 +03:00
|
|
|
else:
|
2017-10-21 00:00:28 +03:00
|
|
|
table[syscall][index[tokens[3]]] = str(counters[key])
|
2017-08-25 22:41:41 +03:00
|
|
|
|
|
|
|
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'
|
|
|
|
]
|
|
|
|
|
2017-10-21 00:00:28 +03:00
|
|
|
def with_microsecond_units(i):
|
|
|
|
if i:
|
|
|
|
return str(i) + u" \u03BCs" # mu for micro
|
|
|
|
else:
|
|
|
|
return str(i) + ' '
|
|
|
|
|
2017-08-25 22:41:41 +03:00
|
|
|
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]]
|
2017-10-21 00:00:28 +03:00
|
|
|
table[syscall][i][j] = with_microsecond_units(counters[key])
|
2017-08-25 22:41:41 +03:00
|
|
|
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)
|