44"""
55@author Eric Bullen <ebullen@linkedin.com>
66@application jtune.py
7- @version 2 .0.3
7+ @version 3 .0.0
88@abstract This tool will give detailed information about the running
99 JVM in real-time. It produces useful information that can
1010 further assist the user in debugging and optimization.
2626import logging
2727import math
2828import os
29- import pickle
3029import re
3130import resource
3231import shlex
@@ -459,7 +458,7 @@ def reduce_k(size=None, precision=2, short_form=True, _place_holder=0):
459458 return "{0} {1}" .format (value , iec_scale [_place_holder ])
460459
461460
462- def _run_analysis (gc_data = None , jmap_data = None , jstat_data = None , proc_details = None , replay_file = None , optimized_for_ygcs_rate = None ):
461+ def _run_analysis (gc_data = None , jmap_data = None , jstat_data = None , proc_details = None , optimized_for_ygcs_rate = None ):
463462 """The meat-and-potatoes of this tool. This takes in numerous data structures,
464463 and prints out a report of the analysis of them."""
465464
@@ -1676,7 +1675,7 @@ def _get_widths(jstat_data=None, short_fields=False):
16761675 return widths
16771676
16781677
1679- def _at_exit (raw_gc_log = None , jmap_data = None , jstat_data = None , proc_details = None , replay_file = None , optimized_for_ygcs_rate = None ):
1678+ def _at_exit (raw_gc_log = None , jmap_data = None , jstat_data = None , proc_details = None , optimized_for_ygcs_rate = None ):
16801679 """The exit function that is called when the user presses ctrl-c, or when it exits after X number
16811680 of jstat iterations. It calls various functions to display useful information to the end-user."""
16821681
@@ -1728,7 +1727,7 @@ def _at_exit(raw_gc_log=None, jmap_data=None, jstat_data=None, proc_details=None
17281727 if in_stanza :
17291728 entry .append (line )
17301729
1731- _run_analysis (gc_data , jmap_data , jstat_data , proc_details , replay_file , optimized_for_ygcs_rate )
1730+ _run_analysis (gc_data , jmap_data , jstat_data , proc_details , optimized_for_ygcs_rate )
17321731
17331732
17341733def get_rotated_log_file (gc_log_file ):
@@ -1808,25 +1807,18 @@ def main():
18081807 else :
18091808 group = parser .add_mutually_exclusive_group (required = True )
18101809
1811- group .add_argument ('-r' , '--replay' , dest = "replay_file" , const = "/tmp/jtune_data-{0}.bin.bz2" .format (user ), help = "Replay a previously saved default is /tmp/jtune_data-{0}.bin.bz2 file" .format (user ), metavar = "FILE" , nargs = "?" , default = None )
18121810 group .add_argument ('-p' , '--pid' , help = 'Which java PID should I attach to' , type = int )
18131811 group .add_argument ('--gc-stdin' , help = 'Read GC log data from stdin' , action = "store_true" )
18141812
18151813 cmd_args = parser .parse_args ()
18161814
1817- replay_file = cmd_args .replay_file
18181815 raw_gc_log_data = list ()
18191816 jmap_data = list ()
18201817 jstat_data = list ()
18211818 proc_details = list ()
18221819
1823- if DEBUG :
1824- # Need to define it here for Pycharm (so I don't
1825- # have to set up arguments).
1826- replay_file = "/tmp/some_file"
1827-
1828- if not (cmd_args .pid or cmd_args .gc_stdin ) and not os .path .isfile (replay_file ):
1829- logger .error ("The replay file '{0}' does not exist, or is not a file." .format (replay_file ))
1820+ if not (cmd_args .pid or cmd_args .gc_stdin ):
1821+ logger .error ("Please specify -p (pid) or --gc-stdin" )
18301822 sys .exit (1 )
18311823
18321824 # A ygc of 1/min
@@ -1853,96 +1845,74 @@ def main():
18531845 logger .error ("You must specify -s, -y, or -c arguments for this option to work." )
18541846 sys .exit (1 )
18551847
1856- if replay_file :
1848+ if not cmd_args . gc_stdin :
18571849 try :
1858- with open (replay_file , "rb" ) as _file :
1859- proc_details , jstat_data , display .display_output , jmap_data , raw_gc_log_data = pickle .loads (_file .read ().decode ('bz2' ))
1860- except (ValueError , IOError ):
1861- logger .error ("I was not able to read the replay file. Exiting." )
1862- sys .exit (1 )
1863- else :
1864- print "* Note: Used cached data found in {0}." .format (replay_file )
1865- else :
1866- if not cmd_args .gc_stdin :
1867- try :
1868- config_error = False
1869- proc_details = get_proc_info (cmd_args .pid )
1870-
1871- java_path , proc_uptime = proc_details ['java_path' ], proc_details ['proc_uptime_seconds' ]
1850+ config_error = False
1851+ proc_details = get_proc_info (cmd_args .pid )
18721852
1873- if proc_details .get ("min_heap_size" , 0 ) != proc_details .get ("max_heap_size" , 1 ):
1874- config_error = True
1875- logger .error (
1876- "It looks like either you didn't specify your min and max heap size (-Xms & -Xmx respectively), or they are set to two different sizes. They need to be set to the same for jtune.py to work properly." )
1853+ java_path , proc_uptime = proc_details ['java_path' ], proc_details ['proc_uptime_seconds' ]
18771854
1878- if not proc_details .get ("print_gc_date_stamps" , False ):
1879- config_error = True
1880- logger .error ("You need to include the '-XX:PrintGCDateStamps' option to the JVM for JTune to work correctly." )
1855+ if proc_details .get ("min_heap_size" , 0 ) != proc_details .get ("max_heap_size" , 1 ):
1856+ config_error = True
1857+ logger .error (
1858+ "It looks like either you didn't specify your min and max heap size (-Xms & -Xmx respectively), or they are set to two different sizes. They need to be set to the same for jtune.py to work properly." )
18811859
1882- if not proc_details .get ("print_gc_details " , False ):
1883- config_error = True
1884- logger .error ("You need to include the '-XX:PrintGCDetails ' option to the JVM for JTune to work correctly." )
1860+ if not proc_details .get ("print_gc_date_stamps " , False ):
1861+ config_error = True
1862+ logger .error ("You need to include the '-XX:PrintGCDateStamps ' option to the JVM for JTune to work correctly." )
18851863
1886- if not proc_details .get ("print_tenuring_distribution " , False ):
1887- config_error = True
1888- logger .error ("You need to include the '-XX:+PrintTenuringDistribution ' option to the JVM for JTune to work correctly." )
1864+ if not proc_details .get ("print_gc_details " , False ):
1865+ config_error = True
1866+ logger .error ("You need to include the '-XX:PrintGCDetails ' option to the JVM for JTune to work correctly." )
18891867
1890- if not proc_details .get ("survivor_ratio" , False ):
1891- logger .warning ("You probably want to include the '-XX:SurvivorRatio=<num>' option to the JVM for JTune to work correctly." )
1868+ if not proc_details .get ("print_tenuring_distribution" , False ):
1869+ config_error = True
1870+ logger .error ("You need to include the '-XX:+PrintTenuringDistribution' option to the JVM for JTune to work correctly." )
18921871
1893- if not proc_details .get ("use_cms" , False ):
1894- config_error = True
1895- logger .error ("You need to include the '-XX:+UseConcMarkSweepGC' option to the JVM for JTune to work correctly." )
1872+ if not proc_details .get ("survivor_ratio" , False ):
1873+ logger .warning ("You probably want to include the '-XX:SurvivorRatio=<num>' option to the JVM for JTune to work correctly." )
18961874
1897- if not proc_details .get ("use_parnew " , False ):
1898- config_error = True
1899- logger .error ("You need to include the '-XX:+UseParNewGC ' option to the JVM for JTune to work correctly." )
1875+ if not proc_details .get ("use_cms " , False ):
1876+ config_error = True
1877+ logger .error ("You need to include the '-XX:+UseConcMarkSweepGC ' option to the JVM for JTune to work correctly." )
19001878
1901- if config_error :
1902- logger . error ( "Exiting." )
1903- sys . exit ( 1 )
1879+ if not proc_details . get ( "use_parnew" , False ) :
1880+ config_error = True
1881+ logger . error ( "You need to include the '-XX:+UseParNewGC' option to the JVM for JTune to work correctly." )
19041882
1905- except ( TypeError , KeyError ) :
1906- logger .error ("I was not able to get the process data for pid {0}" . format ( cmd_args . pid ) )
1883+ if config_error :
1884+ logger .error ("Exiting." )
19071885 sys .exit (1 )
19081886
1909- ###########################################
1910- # Start the gc log watching in a subprocess
1911- back_secs = 300
1912- gc_log_file = get_gc_log_file (proc_details )
1887+ except (TypeError , KeyError ):
1888+ logger .error ("I was not able to get the process data for pid {0}" .format (cmd_args .pid ))
1889+ sys .exit (1 )
19131890
1914- if not gc_log_file :
1915- logger .error ("\n " .join (textwrap .wrap ("I was not able to find a GC log for this process. Is the instance up?" , display .textwrap_offset )))
1916- sys .exit (1 )
1891+ ###########################################
1892+ # Start the gc log watching in a subprocess
1893+ back_secs = 300
1894+ gc_log_file = get_gc_log_file (proc_details )
19171895
1918- ####################################################
1919- # Get the file offset before starting jstat, so
1920- # I can use it after jstat runs to read the log file
1921- gc_log_file_pos = os .stat (gc_log_file ).st_size
1896+ if not gc_log_file :
1897+ logger .error ("\n " .join (textwrap .wrap ("I was not able to find a GC log for this process. Is the instance up?" , display .textwrap_offset )))
1898+ sys .exit (1 )
19221899
1923- jmap_data = get_jmap_data (cmd_args .pid , proc_details )
1900+ ####################################################
1901+ # Get the file offset before starting jstat, so
1902+ # I can use it after jstat runs to read the log file
1903+ gc_log_file_pos = os .stat (gc_log_file ).st_size
19241904
1925- if cmd_args .no_jstat_output :
1926- jstat_data = dict ()
1927- else :
1928- jstat_data = run_jstat (cmd_args .pid , java_path , cmd_args .no_jstat_output , cmd_args .fgc_stop_count , cmd_args .stop_count , cmd_args .ygc_stop_count )
1905+ jmap_data = get_jmap_data (cmd_args .pid , proc_details )
19291906
1930- # This basically hits after the user ctrl-c's
1931- raw_gc_log_data = process_gclog (gc_log_file , gc_log_file_pos )
1907+ if cmd_args .no_jstat_output :
1908+ jstat_data = dict ()
1909+ else :
1910+ jstat_data = run_jstat (cmd_args .pid , java_path , cmd_args .no_jstat_output , cmd_args .fgc_stop_count , cmd_args .stop_count , cmd_args .ygc_stop_count )
19321911
1933- #####################################################
1934- # Keep the last dump of data in case there's an issue
1935- try :
1936- with open ("/tmp/jtune_data-{0}.bin.bz2" .format (user ), "wb" ) as _file :
1937- os .chmod ("/tmp/jtune_data-{0}.bin.bz2" .format (user ), 0666 )
1938- _file .write (pickle .dumps ((proc_details , jstat_data , display .display_output , jmap_data , raw_gc_log_data ), pickle .HIGHEST_PROTOCOL ).encode ('bz2' ))
1939- except IOError as msg :
1940- logger .error ("\n " .join (textwrap .wrap ("I was not able to write to /tmp/jtune_data-{0}.bin.bz2 (no saving of state): {1}" .format (user , msg ), display .textwrap_offset )))
1912+ # This basically hits after the user ctrl-c's
1913+ raw_gc_log_data = process_gclog (gc_log_file , gc_log_file_pos )
19411914
1942- if DEBUG :
1943- _at_exit (raw_gc_log_data , jmap_data , jstat_data , proc_details , replay_file , optimized_for_ygcs_rate )
1944- else :
1945- atexit .register (_at_exit , raw_gc_log_data , jmap_data , jstat_data , proc_details , replay_file , optimized_for_ygcs_rate )
1915+ atexit .register (_at_exit , raw_gc_log_data , jmap_data , jstat_data , proc_details , optimized_for_ygcs_rate )
19461916
19471917
19481918if __name__ == '__main__' :
0 commit comments