require 'optparse' require 'json' require 'tempfile' require 'tmpdir' module PerfMonger module Command class PlotCommand < BaseCommand register_command 'plot', "Plot system performance graphs collected by 'record'" def initialize @parser = OptionParser.new @parser.banner = </dev/null 2>&1') puts("ERROR: convert(1) not found.") puts("ERROR: ImageMagick is required for #{typ}") puts(@parser.help) exit(false) end @output_type = typ end @parser.on('-p', '--prefix PREFIX', 'Output file name prefix.') do |prefix| if ! (prefix =~ /-\Z/) prefix += '-' end @output_prefix = prefix end @parser.on('-s', '--save', 'Save GNUPLOT and data files.') do @save_gpfiles = true end @parser.parse!(argv) if argv.size == 0 puts("ERROR: PerfMonger log file is required") puts(@parser.help) exit(false) end @data_file = File.expand_path(argv.shift) end def run(argv) parse_args(argv) unless system('which gnuplot >/dev/null 2>&1') puts("ERROR: gnuplot not found") puts(@parser.help) exit(false) end unless system('gnuplot -e "set terminal" < /dev/null 2>&1 | grep pdfcairo >/dev/null 2>&1') puts("ERROR: pdfcairo is not supported by installed gnuplot") puts("ERROR: PerfMonger requires pdfcairo-supported gnuplot") puts(@parser.help) exit(false) end player_bin = ::PerfMonger::Command::CoreFinder.player() tmpfile = Tempfile.new("jsondata") IO.popen([player_bin, @data_file], "r").each_line do |line| tmpfile.print(line) end tmpfile.flush() plot_ioinfo(tmpfile.path) plot_cpuinfo(tmpfile.path) end private def plot_ioinfo(json_file) iops_pdf_filename = @output_prefix + 'iops.pdf' transfer_pdf_filename = @output_prefix + 'transfer.pdf' gp_filename = @output_prefix + 'io.gp' dat_filename = @output_prefix + 'io.dat' if @output_type != 'pdf' iops_img_filename = @output_prefix + 'iops.' + @output_type transfer_img_filename = @output_prefix + 'transfer.' + @output_type else iops_img_filename = nil transfer_img_filename = nil end Dir.mktmpdir do |working_dir| Dir.chdir(working_dir) do datafile = File.open(dat_filename, 'w') gpfile = File.new(gp_filename, 'w') start_time = nil devices = nil File.open(json_file).each_line do |line| record = JSON.parse(line) time = record["time"] diskinfo = record["disk"] return unless diskinfo start_time ||= time devices ||= diskinfo["devices"] datafile.puts([time - start_time, devices.map{|device| [diskinfo[device]["riops"], diskinfo[device]["wiops"], diskinfo[device]["rkbyteps"] * 512 / 1024 / 1024, # in MB/s diskinfo[device]["wkbyteps"] * 512 / 1024 / 1024, # in MB/s ] }].flatten.map(&:to_s).join("\t")) end datafile.close col_idx = 2 iops_plot_stmt_list = devices.map do |device| plot_stmt = [] plot_stmt.push("\"#{dat_filename}\" usi 1:#{col_idx} with lines lw 2 title \"#{device} read\"") plot_stmt.push("\"#{dat_filename}\" usi 1:#{col_idx + 1} with lines lw 2 title \"#{device} write\"") col_idx += 4 plot_stmt end.flatten col_idx = 4 transfer_plot_stmt_list = devices.map do |device| plot_stmt = [] plot_stmt.push("\"#{dat_filename}\" usi 1:#{col_idx} with lines lw 2 title \"#{device} read\"") plot_stmt.push("\"#{dat_filename}\" usi 1:#{col_idx + 1} with lines lw 2 title \"#{device} write\"") col_idx += 4 plot_stmt end.flatten gpfile.puts <