# HG changeset patch # User Yasumasa Suenaga # Date 1569563223 -32400 # Node ID dd85c1cbc8c87d15c826754c1d507048ad22e6fc # Parent 65602e1c057df11163bdba5506430e78302f0d2b Bug 3752: Migrate to OpenJFX 13 Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/144 diff -r 65602e1c057d -r dd85c1cbc8c8 ChangeLog --- a/ChangeLog Thu Jul 25 00:06:16 2019 +0900 +++ b/ChangeLog Fri Sep 27 14:47:03 2019 +0900 @@ -1,3 +1,7 @@ +2019-09-27 Yasumasa Suenaga + + * Bug 3752: Migrate to OpenJFX 13 + 2019-07-24 KUBOTA Yuji * Bug 3747: Add note to avoid misrecognized about ResourceExhausted diff -r 65602e1c057d -r dd85c1cbc8c8 Makefile.in --- a/Makefile.in Thu Jul 25 00:06:16 2019 +0900 +++ b/Makefile.in Fri Sep 27 14:47:03 2019 +0900 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -98,8 +98,7 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d -CONFIG_CLEAN_FILES = analyzer/cli/heapstats-cli \ - analyzer/fx/heapstats-analyzer +CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -135,7 +134,7 @@ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - cscope distdir dist dist-all distcheck + cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is @@ -160,10 +159,9 @@ am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/./m4/compile \ $(top_srcdir)/./m4/config.guess $(top_srcdir)/./m4/config.sub \ $(top_srcdir)/./m4/install-sh $(top_srcdir)/./m4/missing \ - $(top_srcdir)/analyzer/cli/heapstats-cli.in \ - $(top_srcdir)/analyzer/fx/heapstats-analyzer.in ./m4/compile \ - ./m4/config.guess ./m4/config.sub ./m4/depcomp ./m4/install-sh \ - ./m4/missing AUTHORS COPYING ChangeLog INSTALL NEWS README + ./m4/compile ./m4/config.guess ./m4/config.sub ./m4/depcomp \ + ./m4/install-sh ./m4/missing AUTHORS COPYING ChangeLog INSTALL \ + NEWS README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -350,8 +348,8 @@ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -362,10 +360,6 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): -analyzer/cli/heapstats-cli: $(top_builddir)/config.status $(top_srcdir)/analyzer/cli/heapstats-cli.in - cd $(top_builddir) && $(SHELL) ./config.status $@ -analyzer/fx/heapstats-analyzer: $(top_builddir)/config.status $(top_srcdir)/analyzer/fx/heapstats-analyzer.in - cd $(top_builddir) && $(SHELL) ./config.status $@ # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. @@ -473,7 +467,10 @@ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ diff -r 65602e1c057d -r dd85c1cbc8c8 README --- a/README Thu Jul 25 00:06:16 2019 +0900 +++ b/README Fri Sep 27 14:47:03 2019 +0900 @@ -70,7 +70,7 @@ If you want to analyze HeapStats SnapShot on CUI environment, you can use HeapStats CLI. - $ java -jar heapstats-cli.jar + $ heapstats-cli If you want to see details, please run heapstats-cli with -help . diff -r 65602e1c057d -r dd85c1cbc8c8 aclocal.m4 --- a/aclocal.m4 Thu Jul 25 00:06:16 2019 +0900 +++ b/aclocal.m4 Fri Sep 27 14:47:03 2019 +0900 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15.1 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,7 +20,7 @@ If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -32,10 +32,10 @@ # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.15' +[am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.15.1], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -51,14 +51,14 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.15.1])dnl +[AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Figure out how to run the assembler. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -78,7 +78,7 @@ # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -130,7 +130,7 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -161,7 +161,7 @@ Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -352,13 +352,12 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. - # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], @@ -366,49 +365,41 @@ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS @@ -417,18 +408,17 @@ # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # -# This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will -# need in order to bootstrap the dependency handling code. +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], - [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) -]) + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -515,8 +505,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. @@ -583,7 +573,7 @@ Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -625,7 +615,7 @@ done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -646,7 +636,7 @@ fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2017 Free Software Foundation, Inc. +# Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -668,7 +658,7 @@ # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -703,7 +693,7 @@ # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -711,49 +701,42 @@ # AM_MAKE_INCLUDE() # ----------------- -# Check to see how make treats includes. +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], -[am_make=${MAKE-make} -cat > confinc << 'END' +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi -AC_SUBST([am__include]) -AC_SUBST([am__quote]) -AC_MSG_RESULT([$_am_result]) -rm -f confinc confmf -]) +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -792,7 +775,7 @@ # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -821,7 +804,7 @@ AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -868,7 +851,7 @@ # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -887,7 +870,7 @@ # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -968,7 +951,7 @@ rm -f conftest.file ]) -# Copyright (C) 2009-2017 Free Software Foundation, Inc. +# Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1028,7 +1011,7 @@ _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1056,7 +1039,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2017 Free Software Foundation, Inc. +# Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1075,7 +1058,7 @@ # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2017 Free Software Foundation, Inc. +# Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -r 65602e1c057d -r dd85c1cbc8c8 agent/Makefile.in --- a/agent/Makefile.in Thu Jul 25 00:06:16 2019 +0900 +++ b/agent/Makefile.in Fri Sep 27 14:47:03 2019 +0900 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -131,7 +131,7 @@ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - distdir + distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is @@ -316,8 +316,8 @@ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -430,7 +430,10 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ diff -r 65602e1c057d -r dd85c1cbc8c8 agent/attacher/Makefile.in --- a/agent/attacher/Makefile.in Thu Jul 25 00:06:16 2019 +0900 +++ b/agent/attacher/Makefile.in Fri Sep 27 14:47:03 2019 +0900 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -257,8 +257,8 @@ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -278,7 +278,10 @@ cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ diff -r 65602e1c057d -r dd85c1cbc8c8 agent/attacher/build.xml --- a/agent/attacher/build.xml Thu Jul 25 00:06:16 2019 +0900 +++ b/agent/attacher/build.xml Fri Sep 27 14:47:03 2019 +0900 @@ -1,7 +1,7 @@ diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/cli/src/main/assembly/distribution.xml --- a/analyzer/cli/src/main/assembly/distribution.xml Thu Jul 25 00:06:16 2019 +0900 +++ b/analyzer/cli/src/main/assembly/distribution.xml Fri Sep 27 14:47:03 2019 +0900 @@ -5,29 +5,31 @@ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> - bin + ${package.os.name}-${os.arch} dir zip - false + true - ${project.build.directory} - heapstats-cli-${project.version} + ${project.build.directory}${file.separator}${imageName} + ${file.separator} + + + ${pom.basedir} + ${file.separator}bin + + heapstats-cli + heapstats-cli.bat + + + + ${project.build.directory}${file.separator}dependency + ${file.separator}${file.separator}mods *.jar - - - false - runtime - false - ${artifact.artifactId}.${artifact.extension} - heapstats-cli-${project.version}/lib - - - diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/cli/src/main/java/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/cli/src/main/java/module-info.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +module heapstats.cli { + requires java.xml.bind; + + requires heapstats.jmx; + requires heapstats.core; +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/core/pom.xml --- a/analyzer/core/pom.xml Thu Jul 25 00:06:16 2019 +0900 +++ b/analyzer/core/pom.xml Fri Sep 27 14:47:03 2019 +0900 @@ -1,7 +1,7 @@ diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/assembly/distribution.xml --- a/analyzer/fx/src/main/assembly/distribution.xml Thu Jul 25 00:06:16 2019 +0900 +++ b/analyzer/fx/src/main/assembly/distribution.xml Fri Sep 27 14:47:03 2019 +0900 @@ -24,43 +24,39 @@ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> - bin + ${package.os.name}-${os.arch} dir zip - false + true - heapstats-analyzer-${project.version} + ${project.build.directory}${file.separator}${imageName} + ${file.separator} + + + ${pom.basedir} + ${file.separator}bin + + heapstats-analyzer + heapstats-analyzer.bat + + + + ${pom.basedir} + ${file.separator} filterDefine.xsd heapstats.properties - ${project.build.directory} - heapstats-analyzer-${project.version} - - *.jar - - - - ${pom.basedir}/lib - heapstats-analyzer-${project.version}/lib + ${project.build.directory}${file.separator}dependency + ${file.separator}${file.separator}mods *.jar - - - false - runtime - false - ${artifact.artifactId}.${artifact.extension} - heapstats-analyzer-${project.version}/lib - - - diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/AboutDialogController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/AboutDialogController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats; - -import java.net.URL; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Accordion; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TitledPane; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.stage.Stage; -import jp.co.ntt.oss.heapstats.plugin.PluginController; - -/** - * FXML Controller of About dialog. - * - * @author Yasumasa Suenaga - */ -public class AboutDialogController implements Initializable { - - @FXML - private Accordion accordion; - - @FXML - private TitledPane pluginPane; - - @FXML - private TableView> pluginTable; - - @FXML - private TableColumn, String> pluinTableNameColumn; - - @FXML - private TableColumn, String> pluginTableLicenseColumn; - - @FXML - private TableView libraryTable; - - @FXML - private TableColumn libraryTablePluginColumn; - - @FXML - private TableColumn libraryTableLibraryColumn; - - @FXML - private TableColumn libraryTableLicenseColumn; - - private Stage stage; - - /** - * Setter method for Stage. - * - * @param stage Instance of main Stage. - */ - public void setStage(Stage stage) { - this.stage = stage; - } - - /** - * Getter method for Stage. - * - * @return stage of this dialog. - */ - public Stage getStage() { - return stage; - } - - public void setPluginInfo(){ - /* - * Set plugin info to pluginTable - * Map.Entry which is implemented in HashMap, Hashtable does not work in TableView. - * Thus I create array of AbstractMap.SimpleEntry . - */ - List> plugins = new ArrayList<>(); - MainWindowController.getInstance().getPluginList().forEach((k, v) -> plugins.add(new AbstractMap.SimpleEntry<>(k, v.getLicense()))); - pluginTable.getItems().addAll(plugins); - - /* Set library license to libraryTable */ - List libraryList = new ArrayList<>(); - MainWindowController.getInstance().getPluginList().forEach((n, c) -> Optional.ofNullable(c.getLibraryLicense()) - .ifPresent(l -> l.forEach((k, v) -> libraryList.add(new PluginController.LibraryLicense(n, k, v))))); - libraryTable.getItems().addAll(libraryList); - } - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - pluinTableNameColumn.setCellValueFactory(new PropertyValueFactory<>("key")); - pluginTableLicenseColumn.setCellValueFactory(new PropertyValueFactory<>("value")); - - libraryTablePluginColumn.setCellValueFactory(new PropertyValueFactory<>("pluginName")); - libraryTableLibraryColumn.setCellValueFactory(new PropertyValueFactory<>("libraryName")); - libraryTableLicenseColumn.setCellValueFactory(new PropertyValueFactory<>("license")); - - accordion.setExpandedPane(pluginPane); - } - - /** - * Event handler when user clickes "OK" button. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onOKClick(ActionEvent event){ - stage.close(); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/HeapStatsFXAnalyzer.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/HeapStatsFXAnalyzer.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats; - -import java.io.InputStream; -import java.util.Optional; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.image.Image; -import javafx.stage.Stage; -import jp.co.ntt.oss.heapstats.utils.HeapStatsConfigException; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * Main class of HeapStats FX Analyzer. - * This class provides entry point of HeapStats FX Analyzer. - * - * @author Yasumasa Suenaga - */ -public class HeapStatsFXAnalyzer extends Application { - - private MainWindowController mainWindowController; - - @Override - public void start(Stage stage) throws Exception { - stage.setTitle("HeapStats Analyzer"); - - try (InputStream icon = getClass().getResourceAsStream("heapstats-icon.png")) { - stage.getIcons().add(new Image(icon)); - } - - Thread.setDefaultUncaughtExceptionHandler((t, e) -> Platform.runLater(() -> HeapStatsUtils.showExceptionDialog(e))); - try { - HeapStatsUtils.load(); - } catch (HeapStatsConfigException e) { - HeapStatsUtils.showExceptionDialog(e); - Runtime.getRuntime().exit(-1); - } - FXMLLoader mainWindowLoader = new FXMLLoader(getClass().getResource("window.fxml"), HeapStatsUtils.getResourceBundle()); - - Parent root = mainWindowLoader.load(); - Scene scene = new Scene(root); - HeapStatsUtils.setWindowController(mainWindowLoader.getController()); - mainWindowController = (MainWindowController) HeapStatsUtils.getWindowController(); - mainWindowController.setOwner(stage); - mainWindowController.setHostServices(getHostServices()); - mainWindowController.loadPlugin(); - - stage.setScene(scene); - stage.show(); - } - - @Override - public void stop() throws Exception { - mainWindowController.getPluginList().values().forEach(c -> Optional.ofNullable(c.getOnCloseRequest()).ifPresent(r -> r.run())); - } - - /** - * Main method of HeapStats analyzer. - * - * @param args the command line arguments - */ - public static void main(String[] args) { - launch(args); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/MainWindowController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/MainWindowController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import javafx.application.HostServices; -import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TextInputDialog; -import javafx.scene.image.Image; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import javafx.stage.Modality; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.stage.Window; -import jp.co.ntt.oss.heapstats.lambda.FunctionWrapper; -import jp.co.ntt.oss.heapstats.plugin.PluginController; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.JVMLiveController; -import jp.co.ntt.oss.heapstats.plugin.builtin.log.LogController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotController; -import jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder.ThreadRecorderController; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * Main window controller. - * - * @author Yasumasa Suenaga - */ -public class MainWindowController implements Initializable, WindowController { - - private Map pluginList; - - private Region veil; - - private ProgressIndicator progress; - - private ClassLoader pluginClassLoader; - - private Window owner; - - @FXML - private StackPane stackPane; - - @FXML - private TabPane tabPane; - - private AboutDialogController aboutDialogController; - - private Scene aboutDialogScene; - - private static MainWindowController thisController; - - private HostServices hostServices; - - @FXML - private void onExitClick(ActionEvent event) { - Platform.exit(); - } - - @FXML - private void onRankLevelClick(ActionEvent event){ - TextInputDialog dialog = new TextInputDialog(Integer.toString(HeapStatsUtils.getRankLevel())); - - try(InputStream icon = getClass().getResourceAsStream("heapstats-icon.png")){ - Stage dialogStage = (Stage)dialog.getDialogPane().getScene().getWindow(); - dialogStage.getIcons().add(new Image(icon)); - } - catch(IOException e){ - HeapStatsUtils.showExceptionDialog(e); - } - - dialog.setTitle("Rank Level setting"); - dialog.setHeaderText("Rank Level setting"); - ResourceBundle resource = ResourceBundle.getBundle("HeapStatsResources", new Locale(HeapStatsUtils.getLanguage())); - dialog.setContentText(resource.getString("rank.label")); - dialog.showAndWait() - .ifPresent(v -> HeapStatsUtils.setRankLevel(Integer.parseInt(v))); - } - - @FXML - private void onHowToClick(ActionEvent event) { - hostServices.showDocument("http://icedtea.classpath.org/wiki/HeapStats/Analyzer-version2"); - } - - @FXML - private void onAboutMenuClick(ActionEvent event){ - Stage dialog = new Stage(StageStyle.UTILITY); - aboutDialogController.setStage(dialog); - - dialog.setScene(aboutDialogScene); - dialog.initModality(Modality.APPLICATION_MODAL); - dialog.setResizable(false); - dialog.setTitle("About HeapStats Analyzer"); - dialog.showAndWait(); - } - - private void addPlugin(String packageName){ - String lastPackageName = packageName.substring(packageName.lastIndexOf('.') + 1); - packageName = packageName.replace('.', '/'); - String fxmlName = packageName + "/" + lastPackageName + ".fxml"; - FXMLLoader loader; - - try{ - ResourceBundle pluginResource = ResourceBundle.getBundle(lastPackageName + "Resources", new Locale(HeapStatsUtils.getLanguage()), pluginClassLoader); - loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName), pluginResource); - } - catch(MissingResourceException e){ - loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName)); - } - - Parent root; - - try { - root = loader.load(); - } - catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - return; - } - - PluginController controller = (PluginController)loader.getController(); - controller.setVeil(veil); - controller.setProgress(progress); - - Tab tab = new Tab(); - tab.setText(controller.getPluginName()); - tab.setContent(root); - tab.setOnSelectionChanged(controller.getOnPluginTabSelected()); - - tabPane.getTabs().add(tab); - - pluginList.put(controller.getPluginName(), controller); - } - - public static MainWindowController getInstance(){ - return thisController; - } - - @FXML - private void onGCAllClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpGCStatisticsToCSV(false); - } - - @FXML - private void onGCSelectedClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpGCStatisticsToCSV(true); - } - - @FXML - private void onHeapAllClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpClassHistogramToCSV(false); - } - - @FXML - private void onHeapSelectedClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpClassHistogramToCSV(true); - } - - @FXML - private void onSnapShotOpenClick(ActionEvent event) { - Tab snapShotTab = tabPane.getTabs().stream() - .filter(t -> t.getText().equals("SnapShot Data")) - .findAny() - .orElseThrow(() -> new IllegalStateException("SnapShot plugin must be loaded.")); - tabPane.getSelectionModel().select(snapShotTab); - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - - snapShotController.onSnapshotFileClick(event); - } - - @FXML - private void onLogOpenClick(ActionEvent event) { - Tab snapShotTab = tabPane.getTabs().stream() - .filter(t -> t.getText().equals("Log Data")) - .findAny() - .orElseThrow(() -> new IllegalStateException("Log plugin must be loaded.")); - tabPane.getSelectionModel().select(snapShotTab); - LogController logController = (LogController)getPluginController("Log Data"); - - logController.onLogFileClick(event); - } - - @FXML - private void onThreadRecorderOpenClick(ActionEvent event) { - Tab threadRecorderTab = tabPane.getTabs().stream() - .filter(t -> t.getText().equals("Thread Recorder")) - .findAny() - .orElseThrow(() -> new IllegalStateException("Thread Recorder plugin must be loaded.")); - tabPane.getSelectionModel().select(threadRecorderTab); - ThreadRecorderController threadRecorderController = (ThreadRecorderController)getPluginController("Thread Recorder"); - - threadRecorderController.onOpenBtnClick(event); - } - - private void initializeAboutDialog(){ - FXMLLoader loader = new FXMLLoader(getClass().getResource("/jp/co/ntt/oss/heapstats/aboutDialog.fxml"), HeapStatsUtils.getResourceBundle()); - - try { - loader.load(); - aboutDialogController = (AboutDialogController)loader.getController(); - aboutDialogScene = new Scene(loader.getRoot()); - } - catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - - } - - @Override - public void initialize(URL url, ResourceBundle rb) { - thisController = this; - pluginList = new ConcurrentHashMap<>(); - veil = new Region(); - veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.2)"); - veil.setVisible(false); - - progress = new ProgressIndicator(); - progress.setMaxSize(200.0d, 200.0d); - progress.setVisible(false); - - stackPane.getChildren().add(veil); - stackPane.getChildren().add(progress); - - initializeAboutDialog(); - } - - /** - * Set HostServices to open HowTo page. - * @param hostServices HostServices - */ - protected void setHostServices(HostServices hostServices) { - this.hostServices = hostServices; - } - - private ClassLoader createPluginClassLoader(String appJarString){ - Path appJarPath; - - try{ - appJarPath = Paths.get(appJarString); - } - catch(InvalidPathException e){ - if((appJarString.charAt(0) == '/') && (appJarString.length() > 2)){ // for Windows - appJarPath = Paths.get(appJarString.substring(1)); - } - else{ - throw e; - } - } - - Path libPath = appJarPath.getParent().resolve("lib"); - URL[] jarURLList = null; - - try(DirectoryStream jarPaths = Files.newDirectoryStream(libPath, "*.jar")){ - jarURLList = StreamSupport.stream(jarPaths.spliterator(), false) - .map(new FunctionWrapper<>(p -> p.toUri().toURL())) - .filter(u -> !u.getFile().endsWith("heapstats-core.jar")) - .filter(u -> !u.getFile().endsWith("heapstats-mbean.jar")) - .filter(u -> !u.getFile().endsWith("jgraphx.jar")) - .collect(Collectors.toList()) - .toArray(new URL[0]); - } - catch(IOException ex) { - Logger.getLogger(MainWindowController.class.getName()).log(Level.SEVERE, null, ex); - } - - return (jarURLList == null) ? MainWindowController.class.getClassLoader() : new URLClassLoader(jarURLList); - } - - /** - * Load plugins which is defined in heapstats.properties. - */ - public void loadPlugin(){ - String resourceName = "/" + this.getClass().getName().replace('.', '/') + ".class"; - String appJarString = this.getClass().getResource(resourceName).getPath(); - - pluginClassLoader = appJarString.contains("!") ? createPluginClassLoader(appJarString.substring(0, appJarString.indexOf('!')).replaceFirst("file:", "")) - : MainWindowController.class.getClassLoader(); - - FXMLLoader.setDefaultClassLoader(pluginClassLoader); - - List plugins = new ArrayList<>(); - /* Add built-in plugins */ - plugins.add(LogController.class.getPackage().getName()); - plugins.add(SnapShotController.class.getPackage().getName()); - plugins.add(ThreadRecorderController.class.getPackage().getName()); - plugins.add(JVMLiveController.class.getPackage().getName()); - /* Add customized plugins by config */ - plugins.addAll(HeapStatsUtils.getPlugins()); - plugins.stream().distinct().forEach(s -> addPlugin(s)); - - aboutDialogController.setPluginInfo(); - } - - /** - * Get controller instance of plugin. - * - * @param pluginName Plugin name which you want. - * @return Controller of Plugin. If it does not exist, return null. - */ - public PluginController getPluginController(String pluginName){ - return pluginList.get(pluginName); - } - - /** - * Get loaded plugin list. - * - * @return Loaded plugin list. - */ - public Map getPluginList() { - return pluginList; - } - - /** - * Select plugin tab - * - * @param pluginName Name of plugin to active. - */ - public void selectTab(String pluginName) throws IllegalArgumentException{ - Tab target = tabPane.getTabs().stream() - .filter(t -> t.getText().equals(pluginName)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException(pluginName + " is not loaded.")); - tabPane.getSelectionModel().select(target); - } - - public Window getOwner() { - return owner; - } - - public void setOwner(Window owner) { - this.owner = owner; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/AboutDialogController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/AboutDialogController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx; + +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Accordion; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TitledPane; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Stage; +import jp.co.ntt.oss.heapstats.api.plugin.PluginController; + +/** + * FXML Controller of About dialog. + * + * @author Yasumasa Suenaga + */ +public class AboutDialogController implements Initializable { + + @FXML + private Accordion accordion; + + @FXML + private TitledPane pluginPane; + + @FXML + private TableView> pluginTable; + + @FXML + private TableColumn, String> pluinTableNameColumn; + + @FXML + private TableColumn, String> pluginTableLicenseColumn; + + @FXML + private TableView libraryTable; + + @FXML + private TableColumn libraryTablePluginColumn; + + @FXML + private TableColumn libraryTableLibraryColumn; + + @FXML + private TableColumn libraryTableLicenseColumn; + + private Stage stage; + + /** + * Setter method for Stage. + * + * @param stage Instance of main Stage. + */ + public void setStage(Stage stage) { + this.stage = stage; + } + + /** + * Getter method for Stage. + * + * @return stage of this dialog. + */ + public Stage getStage() { + return stage; + } + + public void setPluginInfo(){ + /* + * Set plugin info to pluginTable + * Map.Entry which is implemented in HashMap, Hashtable does not work in TableView. + * Thus I create array of AbstractMap.SimpleEntry . + */ + List> plugins = new ArrayList<>(); + MainWindowController.getInstance().getPluginList().forEach((k, v) -> plugins.add(new AbstractMap.SimpleEntry<>(k, v.getLicense()))); + pluginTable.getItems().addAll(plugins); + + /* Set library license to libraryTable */ + List libraryList = new ArrayList<>(); + MainWindowController.getInstance().getPluginList().forEach((n, c) -> Optional.ofNullable(c.getLibraryLicense()) + .ifPresent(l -> l.forEach((k, v) -> libraryList.add(new PluginController.LibraryLicense(n, k, v))))); + libraryTable.getItems().addAll(libraryList); + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + pluinTableNameColumn.setCellValueFactory(new PropertyValueFactory<>("key")); + pluginTableLicenseColumn.setCellValueFactory(new PropertyValueFactory<>("value")); + + libraryTablePluginColumn.setCellValueFactory(new PropertyValueFactory<>("pluginName")); + libraryTableLibraryColumn.setCellValueFactory(new PropertyValueFactory<>("libraryName")); + libraryTableLicenseColumn.setCellValueFactory(new PropertyValueFactory<>("license")); + + accordion.setExpandedPane(pluginPane); + } + + /** + * Event handler when user clickes "OK" button. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onOKClick(ActionEvent event){ + stage.close(); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/HeapStatsFXAnalyzer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/HeapStatsFXAnalyzer.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx; + +import java.io.InputStream; +import java.util.Optional; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.stage.Stage; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsConfigException; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * Main class of HeapStats FX Analyzer. + * This class provides entry point of HeapStats FX Analyzer. + * + * @author Yasumasa Suenaga + */ +public class HeapStatsFXAnalyzer extends Application { + + private MainWindowController mainWindowController; + + @Override + public void start(Stage stage) throws Exception { + stage.setTitle("HeapStats Analyzer"); + + try (InputStream icon = getClass().getResourceAsStream("heapstats-icon.png")) { + stage.getIcons().add(new Image(icon)); + } + + Thread.setDefaultUncaughtExceptionHandler((t, e) -> Platform.runLater(() -> HeapStatsUtils.showExceptionDialog(e))); + try { + HeapStatsUtils.load(); + } catch (HeapStatsConfigException e) { + HeapStatsUtils.showExceptionDialog(e); + Runtime.getRuntime().exit(-1); + } + FXMLLoader mainWindowLoader = new FXMLLoader(getClass().getResource("window.fxml"), HeapStatsUtils.getResourceBundle()); + + Parent root = mainWindowLoader.load(); + Scene scene = new Scene(root); + HeapStatsUtils.setWindowController(mainWindowLoader.getController()); + mainWindowController = (MainWindowController) HeapStatsUtils.getWindowController(); + mainWindowController.setOwner(stage); + mainWindowController.setHostServices(getHostServices()); + mainWindowController.loadPlugin(); + + stage.setScene(scene); + stage.show(); + } + + @Override + public void stop() throws Exception { + mainWindowController.getPluginList().values().forEach(c -> Optional.ofNullable(c.getOnCloseRequest()).ifPresent(r -> r.run())); + } + + /** + * Main method of HeapStats analyzer. + * + * @param args the command line arguments + */ + public static void main(String[] args) { + launch(args); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/MainWindowController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/MainWindowController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javafx.application.HostServices; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TextInputDialog; +import javafx.scene.image.Image; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.Window; +import jp.co.ntt.oss.heapstats.api.WindowController; +import jp.co.ntt.oss.heapstats.lambda.FunctionWrapper; +import jp.co.ntt.oss.heapstats.api.plugin.PluginController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.JVMLiveController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.LogController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.SnapShotController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.threadrecorder.ThreadRecorderController; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * Main window controller. + * + * @author Yasumasa Suenaga + */ +public class MainWindowController implements Initializable, WindowController { + + private Map pluginList; + + private Region veil; + + private ProgressIndicator progress; + + private ClassLoader pluginClassLoader; + + private Window owner; + + @FXML + private StackPane stackPane; + + @FXML + private TabPane tabPane; + + private AboutDialogController aboutDialogController; + + private Scene aboutDialogScene; + + private static MainWindowController thisController; + + private HostServices hostServices; + + @FXML + private void onExitClick(ActionEvent event) { + Platform.exit(); + } + + @FXML + private void onRankLevelClick(ActionEvent event){ + TextInputDialog dialog = new TextInputDialog(Integer.toString(HeapStatsUtils.getRankLevel())); + + try(InputStream icon = getClass().getResourceAsStream("heapstats-icon.png")){ + Stage dialogStage = (Stage)dialog.getDialogPane().getScene().getWindow(); + dialogStage.getIcons().add(new Image(icon)); + } + catch(IOException e){ + HeapStatsUtils.showExceptionDialog(e); + } + + dialog.setTitle("Rank Level setting"); + dialog.setHeaderText("Rank Level setting"); + ResourceBundle resource = ResourceBundle.getBundle("HeapStatsResources", new Locale(HeapStatsUtils.getLanguage())); + dialog.setContentText(resource.getString("rank.label")); + dialog.showAndWait() + .ifPresent(v -> HeapStatsUtils.setRankLevel(Integer.parseInt(v))); + } + + @FXML + private void onHowToClick(ActionEvent event) { + hostServices.showDocument("http://icedtea.classpath.org/wiki/HeapStats/Analyzer-version2"); + } + + @FXML + private void onAboutMenuClick(ActionEvent event){ + Stage dialog = new Stage(StageStyle.UTILITY); + aboutDialogController.setStage(dialog); + + dialog.setScene(aboutDialogScene); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.setResizable(false); + dialog.setTitle("About HeapStats Analyzer"); + dialog.showAndWait(); + } + + private void addPlugin(String packageName){ + String lastPackageName = packageName.substring(packageName.lastIndexOf('.') + 1); + packageName = packageName.replace('.', '/'); + String fxmlName = packageName + "/" + lastPackageName + ".fxml"; + FXMLLoader loader; + + try{ + ResourceBundle pluginResource = ResourceBundle.getBundle(lastPackageName + "Resources", new Locale(HeapStatsUtils.getLanguage()), pluginClassLoader); + loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName), pluginResource); + } + catch(MissingResourceException e){ + loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName)); + } + + Parent root; + + try { + root = loader.load(); + } + catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + return; + } + + PluginController controller = (PluginController)loader.getController(); + controller.setVeil(veil); + controller.setProgress(progress); + + Tab tab = new Tab(); + tab.setText(controller.getPluginName()); + tab.setContent(root); + tab.setOnSelectionChanged(controller.getOnPluginTabSelected()); + + tabPane.getTabs().add(tab); + + pluginList.put(controller.getPluginName(), controller); + } + + public static MainWindowController getInstance(){ + return thisController; + } + + @FXML + private void onGCAllClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpGCStatisticsToCSV(false); + } + + @FXML + private void onGCSelectedClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpGCStatisticsToCSV(true); + } + + @FXML + private void onHeapAllClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpClassHistogramToCSV(false); + } + + @FXML + private void onHeapSelectedClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpClassHistogramToCSV(true); + } + + @FXML + private void onSnapShotOpenClick(ActionEvent event) { + Tab snapShotTab = tabPane.getTabs().stream() + .filter(t -> t.getText().equals("SnapShot Data")) + .findAny() + .orElseThrow(() -> new IllegalStateException("SnapShot plugin must be loaded.")); + tabPane.getSelectionModel().select(snapShotTab); + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + + snapShotController.onSnapshotFileClick(event); + } + + @FXML + private void onLogOpenClick(ActionEvent event) { + Tab snapShotTab = tabPane.getTabs().stream() + .filter(t -> t.getText().equals("Log Data")) + .findAny() + .orElseThrow(() -> new IllegalStateException("Log plugin must be loaded.")); + tabPane.getSelectionModel().select(snapShotTab); + LogController logController = (LogController)getPluginController("Log Data"); + + logController.onLogFileClick(event); + } + + @FXML + private void onThreadRecorderOpenClick(ActionEvent event) { + Tab threadRecorderTab = tabPane.getTabs().stream() + .filter(t -> t.getText().equals("Thread Recorder")) + .findAny() + .orElseThrow(() -> new IllegalStateException("Thread Recorder plugin must be loaded.")); + tabPane.getSelectionModel().select(threadRecorderTab); + ThreadRecorderController threadRecorderController = (ThreadRecorderController)getPluginController("Thread Recorder"); + + threadRecorderController.onOpenBtnClick(event); + } + + private void initializeAboutDialog(){ + FXMLLoader loader = new FXMLLoader(getClass().getResource("/jp/co/ntt/oss/heapstats/fx/aboutDialog.fxml"), HeapStatsUtils.getResourceBundle()); + + try { + loader.load(); + aboutDialogController = (AboutDialogController)loader.getController(); + aboutDialogScene = new Scene(loader.getRoot()); + } + catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + + } + + @Override + public void initialize(URL url, ResourceBundle rb) { + thisController = this; + pluginList = new ConcurrentHashMap<>(); + veil = new Region(); + veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.2)"); + veil.setVisible(false); + + progress = new ProgressIndicator(); + progress.setMaxSize(200.0d, 200.0d); + progress.setVisible(false); + + stackPane.getChildren().add(veil); + stackPane.getChildren().add(progress); + + initializeAboutDialog(); + } + + /** + * Set HostServices to open HowTo page. + * @param hostServices HostServices + */ + protected void setHostServices(HostServices hostServices) { + this.hostServices = hostServices; + } + + private ClassLoader createPluginClassLoader(String appJarString){ + Path appJarPath; + + try{ + appJarPath = Paths.get(appJarString); + } + catch(InvalidPathException e){ + if((appJarString.charAt(0) == '/') && (appJarString.length() > 2)){ // for Windows + appJarPath = Paths.get(appJarString.substring(1)); + } + else{ + throw e; + } + } + + Path libPath = appJarPath.getParent(); + URL[] jarURLList = null; + + try(DirectoryStream jarPaths = Files.newDirectoryStream(libPath, "*.jar")){ + jarURLList = StreamSupport.stream(jarPaths.spliterator(), false) + .map(new FunctionWrapper<>(p -> p.toUri().toURL())) + .filter(u -> !u.getFile().startsWith("heapstats-")) + .filter(u -> !u.getFile().startsWith("javax.activation-api")) + .filter(u -> !u.getFile().startsWith("jaxb-api")) + .filter(u -> !u.getFile().startsWith("jgraphx-")) + .collect(Collectors.toList()) + .toArray(new URL[0]); + } + catch(IOException ex) { + Logger.getLogger(MainWindowController.class.getName()).log(Level.SEVERE, null, ex); + } + + return (jarURLList == null) ? MainWindowController.class.getClassLoader() : new URLClassLoader(jarURLList); + } + + /** + * Load plugins which is defined in heapstats.properties. + */ + public void loadPlugin(){ + String resourceName = "/" + this.getClass().getName().replace('.', '/') + ".class"; + String appJarString = this.getClass().getResource(resourceName).getPath(); + + pluginClassLoader = appJarString.contains("!") ? createPluginClassLoader(appJarString.substring(0, appJarString.indexOf('!')).replaceFirst("file://", "")) + : MainWindowController.class.getClassLoader(); + + FXMLLoader.setDefaultClassLoader(pluginClassLoader); + + List plugins = new ArrayList<>(); + /* Add built-in plugins */ + plugins.add(LogController.class.getPackage().getName()); + plugins.add(SnapShotController.class.getPackage().getName()); + plugins.add(ThreadRecorderController.class.getPackage().getName()); + plugins.add(JVMLiveController.class.getPackage().getName()); + /* Add customized plugins by config */ + plugins.addAll(HeapStatsUtils.getPlugins()); + plugins.stream().distinct().forEach(s -> addPlugin(s)); + + aboutDialogController.setPluginInfo(); + } + + /** + * Get controller instance of plugin. + * + * @param pluginName Plugin name which you want. + * @return Controller of Plugin. If it does not exist, return null. + */ + public PluginController getPluginController(String pluginName){ + return pluginList.get(pluginName); + } + + /** + * Get loaded plugin list. + * + * @return Loaded plugin list. + */ + public Map getPluginList() { + return pluginList; + } + + /** + * Select plugin tab + * + * @param pluginName Name of plugin to active. + */ + public void selectTab(String pluginName) throws IllegalArgumentException{ + Tab target = tabPane.getTabs().stream() + .filter(t -> t.getText().equals(pluginName)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(pluginName + " is not loaded.")); + tabPane.getSelectionModel().select(target); + } + + public Window getOwner() { + return owner; + } + + public void setOwner(Window owner) { + this.owner = owner; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/JVMLiveConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/JVMLiveConfig.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Properties; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.errorreporter.ErrorReportServer; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsConfigException; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * Configuration of JVMLive. + * + * @author Yasumasa Suenaga + */ +public class JVMLiveConfig { + + public static final String JDP_WAIT_DURATION_KEY = "jdp.waitDuration"; + + public static final String THREADPOOL_SHOTDOWN_AWAIT_TIME_KEY = "threadpool.shotdownAwaitTime"; + + public static final String ERRORREPORT_SERVER_PORT_KEY = "errorreport.server.port"; + + private static final Properties prop = new Properties(); + + /** + * Check and set value to JVMListConfig. + * + * @param key configuration key. + * @param defaultValue default value of configuration key. + * @throws HeapStatsConfigException when invalid HeapStats config is set. + */ + private static void setupNumericProperty(String key, int defaultValue) throws HeapStatsConfigException{ + String valStr = prop.getProperty(key); + + if(valStr == null){ + prop.setProperty(key, Integer.toString(defaultValue)); + } + else{ + try{ + Integer.decode(valStr); + } + catch(NumberFormatException e){ + throw new HeapStatsConfigException(String.format("Invalid option: %s=%s", key, valStr), e); + } + } + + } + + /** + * Load configuration from <HeapStats home directory>/jvmlive.properties . + * + * @throws IOException when properties file cannot be read. + * @throws HeapStatsConfigException when invalid HeapStats config is set. + */ + public static void load() throws IOException, HeapStatsConfigException{ + Path properties = Paths.get(HeapStatsUtils.getHeapStatsHomeDirectory().toString(), "jvmlive.properties"); + + try(InputStream in = Files.newInputStream(properties, StandardOpenOption.READ)){ + prop.load(in); + } + catch(NoSuchFileException e){ + // use default values. + } + + setupNumericProperty(JDP_WAIT_DURATION_KEY, 3); + setupNumericProperty(THREADPOOL_SHOTDOWN_AWAIT_TIME_KEY, 5); + setupNumericProperty(ERRORREPORT_SERVER_PORT_KEY, ErrorReportServer.DEFAULT_ERROR_REPORT_SERVER_PORT); + } + + /** + * Get a Wait duration for JDP packet. + * This value uses as duration for JdpValidatorService. + * + * @return Wait duration for JDP packet. + */ + public static int getJdpWaitDuration(){ + return Integer.parseInt(prop.getProperty(JDP_WAIT_DURATION_KEY)); + } + + /** + * Get a wait duration time for JDP packet. + * @return Await time for threadpool shutdown. + */ + public static int getThreadpoolShutdownAwaitTime(){ + return Integer.parseInt(prop.getProperty(THREADPOOL_SHOTDOWN_AWAIT_TIME_KEY)); + } + + /** + * Get a prot of error report server. + * @return Port for error report receiver. + */ + public static int getErrorReportServerPort(){ + return Integer.parseInt(prop.getProperty(ERRORREPORT_SERVER_PORT_KEY)); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/JVMLiveController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/JVMLiveController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.rmi.ConnectException; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javafx.beans.binding.Bindings; +import javafx.concurrent.ScheduledService; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Labeled; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextArea; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.image.Image; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.stage.FileChooser; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.util.Duration; +import jp.co.ntt.oss.heapstats.fx.MainWindowController; +import jp.co.ntt.oss.heapstats.jmx.JMXHelper; +import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; +import jp.co.ntt.oss.heapstats.api.plugin.PluginController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.errorreporter.ErrorReportDecoder; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.errorreporter.ErrorReportServer; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp.JdpDecoder; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp.JdpReceiver; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp.JdpTableKeyValue; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp.JdpValidatorService; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.mbean.HeapStatsMBeanController; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsConfigException; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class for JVMLive + * + * @author Yasumasa Suenaga + */ +public class JVMLiveController extends PluginController implements Initializable { + + @FXML + private ListView jvmList; + + @FXML + private TableView detailTable; + + @FXML + private TableColumn jdpKey; + + @FXML + private TableColumn jdpValue; + + @FXML + private ListView crashList; + + @FXML + private MenuItem detailsMenu; + + @FXML + private MenuItem saveMenu; + + private JdpReceiver jdpReceiver; + + private Thread receiverThread; + + private ScheduledService jdpValidator; + + private ExecutorService taskThreadPool; + + private ExecutorService jmxThreadPool; + + private ErrorReportServer errorReportServer; + + private Thread errorReportThread; + + private Optional jconsolePath; + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + + try { + JVMLiveConfig.load(); + } catch (IOException | HeapStatsConfigException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + + jdpKey.setCellValueFactory(new PropertyValueFactory<>("key")); + jdpValue.setCellValueFactory(new PropertyValueFactory<>("value")); + jdpValue.setCellFactory(c -> new TableCell(){ + @Override + protected void updateItem(Object item, boolean empty) { + super.updateItem(item, empty); + Optional.ofNullable(item).ifPresent(i -> { + try{ + if(i instanceof Labeled){ + setGraphic((Labeled)i); + } + else if(i instanceof JMXHelper){ + setGraphic(createHyperlink((JMXHelper)i)); + } + else{ + setText((String)i); + } + } + catch(Throwable t){ + setText(""); + } + }); + } + } + ); + + jvmList.getSelectionModel().selectedItemProperty() + .addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(d -> showJdpDecoderDetail((JdpDecoder)d))); + + jvmList.setCellFactory(l -> new ListCell(){ + @Override + protected void updateItem(JdpDecoder jdp, boolean b) { + super.updateItem(jdp, b); + Optional.ofNullable(jdp) + .ifPresent(d -> { + setText(d.toString()); + styleProperty().bind(Bindings.createStringBinding(() -> d.invalidateProperty().get() ? "-fx-text-fill: orange;" : "-fx-text-fill: black;", d.invalidateProperty())); + }); + } + } + ); + + Path jdkBase = Paths.get(Paths.get(System.getProperties().getProperty("java.home")).getParent().toString(), "bin"); + File f = new File(jdkBase.toString(), "jconsole"); + if(f.canExecute()){ + jconsolePath = Optional.of(f.getAbsolutePath()); + } + else{ + f = new File(jdkBase.toString(), "jconsole.exe"); + if(f.canExecute()){ + jconsolePath = Optional.of(f.getAbsolutePath()); + } + else{ + jconsolePath = Optional.empty(); + } + } + + + taskThreadPool = Executors.newCachedThreadPool(); + jmxThreadPool = Executors.newCachedThreadPool(); + + try { + jdpReceiver = new JdpReceiver(jvmList, taskThreadPool, jconsolePath, jmxThreadPool); + receiverThread = new Thread(jdpReceiver, "JDP receiver thread"); + receiverThread.start(); + + jdpValidator = new JdpValidatorService(jvmList); + jdpValidator.setPeriod(Duration.seconds(2)); + jdpValidator.start(); + } catch (UnknownHostException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + + errorReportServer = new ErrorReportServer(JVMLiveConfig.getErrorReportServerPort(), crashList.getItems(), taskThreadPool); + errorReportThread = new Thread(errorReportServer, "ErrorReport receiver thread"); + errorReportThread.start(); + + detailsMenu.disableProperty().bind(crashList.selectionModelProperty().getValue().selectedItemProperty().isNull()); + saveMenu.disableProperty().bind(crashList.selectionModelProperty().getValue().selectedItemProperty().isNull()); + } + + private void onHeapStatsLinkClicked(JMXHelper jmxHelper){ + ResourceBundle pluginResource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + FXMLLoader loader = new FXMLLoader(JVMLiveController.class.getResource("/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/heapstatsMBean.fxml"), pluginResource); + HeapStatsMBeanController mbeanController; + Scene mbeanDialogScene; + + try { + loader.load(); + mbeanController = (HeapStatsMBeanController)loader.getController(); + mbeanDialogScene = new Scene(loader.getRoot()); + } + catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + return; + } + + mbeanController.setJmxHelper(jmxHelper); + mbeanController.loadAllConfigs(); + Stage dialog = new Stage(StageStyle.UTILITY); + + try(InputStream icon = MainWindowController.class.getResourceAsStream("heapstats-icon.png")){ + dialog.getIcons().add(new Image(icon)); + } + catch(IOException e){ + HeapStatsUtils.showExceptionDialog(e); + } + + dialog.setScene(mbeanDialogScene); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.setResizable(false); + dialog.setTitle("HeapStats configuration @ " + jmxHelper.getUrl().toString()); + dialog.showAndWait(); + } + + private Hyperlink createHyperlink(JMXHelper jmxHelper){ + Hyperlink heapstatsLink = new Hyperlink(jmxHelper.getMbean().getHeapStatsVersion()); + heapstatsLink.setOnAction(e -> onHeapStatsLinkClicked(jmxHelper)); + + return heapstatsLink; + } + + private void showHsErrDialog(ErrorReportDecoder data){ + String host = data.toString(); + String hsErr; + + try(BufferedReader reader = Files.newBufferedReader(data.getHsErrFile().toPath())){ + hsErr = reader.lines().collect(Collectors.joining("\n")); + } catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + return; + } + + Alert dialog = new Alert(AlertType.WARNING); + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + + dialog.setTitle(resource.getString("dialog.crashreport.title")); + dialog.setHeaderText(resource.getString("dialog.crashreport.header") + host); + TextArea hsErrArea = new TextArea(hsErr); + hsErrArea.setEditable(false); + dialog.getDialogPane().setExpandableContent(hsErrArea); + dialog.showAndWait(); + } + + @FXML + private void onCrashHistoryClicked(MouseEvent event){ + if(event.getButton().equals(MouseButton.PRIMARY) && (event.getClickCount() == 2)){ + showHsErrDialog(crashList.getSelectionModel().getSelectedItem()); + } + } + + @FXML + private void onDetailsMenuClicked(ActionEvent event){ + showHsErrDialog(crashList.getSelectionModel().getSelectedItem()); + } + + @FXML + private void onSaveMenuClicked(ActionEvent event){ + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + + dialog.setTitle(resource.getString("dialog.crashreport.save.title")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Log file (*.log)", "*.log"), + new FileChooser.ExtensionFilter("All files", "*.*")); + + Path hs_err_path = crashList.getSelectionModel().getSelectedItem().getHsErrFile().toPath(); + Optional.ofNullable(dialog.showSaveDialog(MainWindowController.getInstance().getOwner())) + .ifPresent(new ConsumerWrapper<>(t -> Files.copy(hs_err_path, t.toPath(), StandardCopyOption.REPLACE_EXISTING))); + } + + private void showJdpDecoderDetail(JdpDecoder data){ + detailTable.setItems(data.jdpTableKeyValueProperty().get()); + } + + + @Override + public String getPluginName() { + return "JVMLive"; + } + + @Override + public String getLicense() { + return PluginController.LICENSE_GPL_V2; + } + + @Override + public Map getLibraryLicense() { + return null; + } + + @Override + public EventHandler getOnPluginTabSelected() { + // do nothing + return null; + } + + private void closeJMXConnection(Object obj){ + if(obj instanceof JMXHelper){ + try{ + ((JMXHelper)obj).close(); + } catch(ConnectException ex){ + // Do nothing + // Runtime will connect remote host when JMX connection will be closed. + // So we can skip this operation. + Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); + } catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + } + } + + @Override + public Runnable getOnCloseRequest() { + return () -> { + Optional.ofNullable(jdpValidator).ifPresent(p -> p.cancel()); + Optional.ofNullable(jdpReceiver).ifPresent(r -> r.cancel(true)); + Optional.ofNullable(receiverThread).ifPresent(new ConsumerWrapper<>(t -> t.join())); + errorReportServer.cancel(true); + try{ + errorReportThread.join(); + } + catch (InterruptedException ex){ + Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); + } + + taskThreadPool.shutdownNow(); + try { + taskThreadPool.awaitTermination(JVMLiveConfig.getThreadpoolShutdownAwaitTime(), TimeUnit.SECONDS); + } catch (InterruptedException ex) { + Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); + } + + jmxThreadPool.shutdownNow(); + try { + jmxThreadPool.awaitTermination(JVMLiveConfig.getThreadpoolShutdownAwaitTime(), TimeUnit.SECONDS); + } catch (InterruptedException ex) { + Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); + } + + jvmList.getItems().forEach(p -> closeJMXConnection(p.getHeapStatsTableKeyValue().valueProperty().get())); + }; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/errorreporter/ErrorReportDecoder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/errorreporter/ErrorReportDecoder.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.errorreporter; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Platform; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; + +/** + * Error report decoder. + * This class can treat data which is generated by -XX:+TransmitErrorReport and -XX:ErrorReportServer=<address>:<port> . + * + * @author Yasumasa Suenaga + */ +public class ErrorReportDecoder extends Task{ + + /** Buffer size for receive buffer. */ + public static final int BUFFER_SIZE = 1024; + + /** + * Created time of this instance. + * This time equals JVM crashed. + */ + private LocalDateTime crashedTime; + + /** IP address for crash JVM. */ + private InetAddress localIP; + + /** User name for crash Java process. */ + private String user; + + private boolean crash; + + private boolean debug; + + private int procCount; + + private int buildVer; + + private int[] resolveTiming; + + private int resolvePlatform; + + /** JDK version for crash JVM. */ + private String jdkVersion; + + /** VM version for crash JVM. */ + private String vmVersion; + + /** Length of this error report. */ + private int reportLength; + + /** + * File instance for this hs_err report. + * This file is temporary. So we add "deleteOnExit" to this field. + */ + private File hsErrFile; + + private final ObservableList crashList; + + /** Async channel to crash JVM. */ + private final AsynchronousSocketChannel ch; + + /** InetSocketAddress for crash JVM. */ + private final InetSocketAddress sockAddr; + + /** + * Constructor of ErrorReportDecorder. + * + * @param crashList List of crash jvms. + * @param ch AsynchronousSocketChannel to crash jvm. + * + * @throws IOException If an I/O error occurs + */ + public ErrorReportDecoder(ObservableList crashList, AsynchronousSocketChannel ch) throws IOException{ + crashedTime = LocalDateTime.now(); + + localIP = null; + user = null; + crash = false; + debug = false; + procCount = -1; + buildVer = -1; + resolveTiming = new int[2]; + resolvePlatform = -1; + jdkVersion = null; + vmVersion = null; + reportLength = -1; + hsErrFile = null; + + this.crashList = crashList; + this.ch = ch; + this.sockAddr = (InetSocketAddress)ch.getRemoteAddress(); + } + + /** + * Parse header information in error report data. + * + * @param buf Received data + */ + private void parseHeader(ByteBuffer buf){ + + /* Check magic number */ + if(buf.getInt() != 0xcafebabe){ + throw new IllegalArgumentException("Magic number of ErrorReport is incorrected: from " + sockAddr.toString()); + } + + /* Get unknown data 1 (do nothing) */ + buf.getInt(); + + /* IP address which error is occurred */ + byte[] rawIP = new byte[4]; + buf.get(rawIP); + try { + localIP = InetAddress.getByAddress(rawIP); + } catch (UnknownHostException ex) { + Logger.getLogger(ErrorReportServer.class.getName()).log(Level.SEVERE, null, ex); + } + + /* User name */ + byte[] rawUserName = new byte[32]; + buf.get(rawUserName); + user = new String(rawUserName, StandardCharsets.UTF_8); + + /* Flags */ + int flags = buf.getInt(); + procCount = flags & 0x07; + buildVer = (flags >> 0x05) & 0x3; + if((flags & 0x80) != 0) crash = true; + if((flags & 0x03) != 0) debug = true; + + /* Resolve timing */ + resolveTiming[0] = buf.getInt(); + resolveTiming[1] = buf.getInt(); + + /* Resolve platform */ + resolvePlatform = buf.getInt(); + + /* Get unknown data 2 (do nothing) */ + buf.getInt(); + + /* Get unknown data 3 (do nothing) */ + buf.getInt(); + + /* Get unknown data 4 (do nothing) */ + buf.getInt(); + + /* Get unknown data 5 (do nothing) */ + buf.getInt(); + + /* Get unknown data 6 (do nothing) */ + buf.getInt(); + + /* Get unknown data 7 (do nothing) */ + buf.getInt(); + + /* Get unknown data 8 (do nothing) */ + buf.getInt(); + + /* Get unknown data 9 (do nothing) */ + buf.getInt(); + + /* Get unknown data 10 (do nothing) */ + buf.getInt(); + + /* JDK version */ + byte[] rawJDKVersion = new byte[buf.getInt()]; + buf.get(rawJDKVersion); + jdkVersion = new String(rawJDKVersion, StandardCharsets.UTF_8); + + /* VM version */ + byte[] rawVMVersion = new byte[buf.getInt()]; + buf.get(rawVMVersion); + vmVersion = new String(rawVMVersion, StandardCharsets.UTF_8); + + /* Length of hs_err report */ + reportLength = buf.getInt(); + } + + public InetAddress getLocalIP() { + return localIP; + } + + public void setLocalIP(InetAddress localIP) { + this.localIP = localIP; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public void setCrash(){ + this.crash = true; + } + + public boolean isCrash(){ + return this.crash; + } + + public void setDebug(){ + this.debug = true; + } + + public boolean isDebug(){ + return this.debug; + } + + public int getProcCount() { + return procCount; + } + + public void setProcCount(int procCount) { + this.procCount = procCount; + } + + public int getBuildVer() { + return buildVer; + } + + public void setBuildVer(int buildVer) { + this.buildVer = buildVer; + } + + public int[] getResolveTiming() { + return resolveTiming; + } + + public void setResolveTiming(int[] resolveTiming) { + this.resolveTiming = resolveTiming; + } + + public int getResolvePlatform() { + return resolvePlatform; + } + + public void setResolvePlatform(int resolvePlatform) { + this.resolvePlatform = resolvePlatform; + } + + public String getJdkVersion() { + return jdkVersion; + } + + public void setJdkVersion(String jdkVersion) { + this.jdkVersion = jdkVersion; + } + + public String getVmVersion() { + return vmVersion; + } + + public void setVmVersion(String vmVersion) { + this.vmVersion = vmVersion; + } + + public int getReportLength() { + return reportLength; + } + + public void setReportLength(int reportLength) { + this.reportLength = reportLength; + } + + public File getHsErrFile() { + return hsErrFile; + } + + public void setHsErrFile(File hsErrFile) { + this.hsErrFile = hsErrFile; + } + + public LocalDateTime getCrashedTime() { + return crashedTime; + } + + public void setCrashedTime(LocalDateTime crashedTime) { + this.crashedTime = crashedTime; + } + + @Override + public String toString() { + return localIP.toString(); + } + + @Override + protected Void call() throws Exception { + + try{ + ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE); + Future bytes = ch.read(buf); + + if(bytes.get() <= 0){ + return null; + } + + buf.flip(); + parseHeader(buf); + + if(reportLength > 0){ + int remaining = reportLength; + File hs_err_log = File.createTempFile("hs_err", sockAddr.getAddress().getHostAddress()); + hs_err_log.deleteOnExit(); + + try(SeekableByteChannel hs_err = Files.newByteChannel(hs_err_log.toPath(), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)){ + + while(remaining > 0){ + int sizeShouldWrite = buf.limit() - buf.position(); + if(sizeShouldWrite > remaining){ + sizeShouldWrite = remaining; + buf.limit(sizeShouldWrite); + } + remaining -= sizeShouldWrite; + + hs_err.write(buf); + buf.flip(); + ch.read(buf).get(); + buf.flip(); + } + + } + + hsErrFile = hs_err_log; + Platform.runLater(() -> crashList.add(this)); + } + + } + finally{ + ch.close(); + } + + return null; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/errorreporter/ErrorReportServer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/errorreporter/ErrorReportServer.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.errorreporter; + +import java.net.InetSocketAddress; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; + +/** + * Service thread to receive crash reports. + * This thread can treat data which is generated by -XX:+TransmitErrorReport and -XX:ErrorReportServer=<address>:<port> . + * + * @author Yasumasa Suenaga + */ +public class ErrorReportServer extends Task{ + + public static final int DEFAULT_ERROR_REPORT_SERVER_PORT = 4711; + + private final int port; + + private final ObservableList crashList; + + private final ExecutorService hsErrProcPool; + + /** + * Constructor of ErrorReportServer. + * + * @param crashList List of crash jvms. + * @param hsErrProcPool ThreadPool which processes ErrorReport. + */ + public ErrorReportServer(ObservableList crashList, ExecutorService hsErrProcPool){ + this(DEFAULT_ERROR_REPORT_SERVER_PORT, crashList, hsErrProcPool); + } + + /** + * Constructor of ErrorReportServer. + * @param port Port number of ErrorReportServer. + * @param crashList List of crash jvms. + * @param hsErrProcPool ThreadPool which processes ErrorReport. + */ + public ErrorReportServer(int port, ObservableList crashList, ExecutorService hsErrProcPool){ + this.port = port; + this.crashList = crashList; + this.hsErrProcPool = hsErrProcPool; + } + + @Override + protected Void call() throws Exception { + + try(AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open()){ + server.bind(new InetSocketAddress(port)); + + while(!isCancelled()){ + Future sock = server.accept(); + hsErrProcPool.submit(new ErrorReportDecoder(crashList, sock.get())); + } + + } + + return null; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpDecoder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpDecoder.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.control.Labeled; +import javafx.scene.control.ListView; +import jp.co.ntt.oss.heapstats.fx.lambda.EventHandlerWrapper; +import jp.co.ntt.oss.heapstats.jmx.JMXHelper; +import jp.co.ntt.oss.heapstats.lambda.RunnableWrapper; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + + +/** + * JDP packet decoder. + * This class containes JDP packet data and method for parse it. + * + * @author Yasumasa Suenaga + */ +public class JdpDecoder extends Task{ + + /** Key of JDP for main class. */ + public static final String MAIN_CLASS_KEY = "MAIN_CLASS"; + + /** Key of JDP for instance name. */ + public static final String INSTANCE_NAME_KEY = "INSTANCE_NAME"; + + /** Key of JDP for JMX URL. */ + public static final String JMX_SERVICE_URL_KEY = "JMX_SERVICE_URL"; + + /** Key of JDP for UUID. */ + public static final String DISCOVERABLE_SESSION_UUID_KEY = "DISCOVERABLE_SESSION_UUID"; + + /** Key of JDP for broadcast interval. */ + public static final String PACKET_BROADCAST_INTERVAL_KEY = "BROADCAST_INTERVAL"; + + /** Key of JDP for PID. */ + public static final String PROCESS_ID_KEY = "PROCESS_ID"; + + /** Raw JDP packet data. */ + private final ByteBuffer jdpRawData; + + /** + * Received time of this instance. + */ + private LocalDateTime receivedTime; + + /** Source address of this JDP packet. */ + private final InetSocketAddress sourceAddr; + + /** Main class of this JDP packet. */ + private String mainClass; + + /** Instance name of this JDP packet. */ + private String instanceName; + + /** JMX URL of this JDP packet. */ + private String jmxServiceURL; + + /** PID of this JDP packet. */ + private int pid; + + /** UUID of this JDP packet. */ + private UUID uuid; + + /** Broadcast interval of this JDP packet. */ + private int broadcastInterval; + + /** If this packet is expired, this flag is set to true by JdpValidatorService. */ + private final BooleanProperty invalidate; + + private final ObjectProperty> jdpTableKeyValue; + + private final ListView jdpList; + + private final Optional jconsolePath; + + private final ExecutorService jmxProcPool; + + private JdpTableKeyValue heapstatsValue; + + /** + * Constructor of JdpDecorder. + * + * @param sourceAddr Source address of JDP packet. + * @param rawData JDP raw data. + * @param jdpList ListView which includes JDP. + * @param jconsolePath Path to JConsole. + * @param jmxProcPool ThreadPool which processes JMX access. + */ + public JdpDecoder(InetSocketAddress sourceAddr, ByteBuffer rawData, ListView jdpList, Optional jconsolePath, ExecutorService jmxProcPool){ + this.receivedTime = LocalDateTime.now(); + this.sourceAddr = sourceAddr; + this.jdpRawData = rawData; + this.jdpList = jdpList; + this.invalidate = new SimpleBooleanProperty(false); + this.jdpTableKeyValue = new SimpleObjectProperty<>(); + this.jconsolePath = jconsolePath; + this.jmxProcPool = jmxProcPool; + } + + /** + * Create String instance from ByteBuffer. + * + * @param buffer ByteBuffer to parse. Position must be set to top of String data. + * @return String from ByteBuffer. + */ + private String getStringFromByteBuffer(ByteBuffer buffer){ + int length = Short.toUnsignedInt(buffer.getShort()); + byte[] rawValue = new byte[length]; + buffer.get(rawValue); + + return new String(rawValue, StandardCharsets.UTF_8); + } + + private Map parseJdpPacket(){ + + if(jdpRawData.getInt() != 0xC0FFEE42){ + throw new RuntimeException("Invalid magic number."); + } + + if(jdpRawData.getShort() != 1){ + throw new RuntimeException("Invalid JDP version."); + } + + Map jdpContents = new HashMap<>(); + + while(jdpRawData.hasRemaining()){ + String key = getStringFromByteBuffer(jdpRawData); + String value = getStringFromByteBuffer(jdpRawData); + + jdpContents.put(key, value); + } + + return jdpContents; + } + + private void setJdpTableKeyValue(JdpTableKeyValue val, Labeled jmxURL){ + switch(val.keyProperty().get()){ + case "Received Time": + val.valueProperty().set(receivedTime.format(HeapStatsUtils.getDateTimeFormatter())); + break; + + case "Address": + val.valueProperty().set(sourceAddr.getAddress().getHostAddress()); + break; + + case "JDP Instance Name": + val.valueProperty().set(instanceName); + break; + + case "Main Class": + val.valueProperty().set(mainClass); + break; + + case "UUID": + val.valueProperty().set(uuid.toString()); + break; + + case "PID": + val.valueProperty().set(Integer.toString(pid)); + break; + + case "JMX URL": + val.valueProperty().set(jmxURL); + } + } + + /** + * Update JDP packet if same UUID is registered in JDP list. + */ + private void updateJDPData(){ + Labeled jmxURL; + + if(jconsolePath.isPresent()){ + String[] execParam = {jconsolePath.get(), jmxServiceURL}; + jmxURL = new Hyperlink(jmxServiceURL); + ((Hyperlink)jmxURL).setOnAction(new EventHandlerWrapper<>(e -> Runtime.getRuntime().exec(execParam))); + } + else{ + jmxURL = new Label(jmxServiceURL); + } + + int idx = jdpList.getItems().indexOf(this); + + if(idx == -1){ + heapstatsValue = new JdpTableKeyValue("HeapStats", "Checking..."); + jmxProcPool.submit(new RunnableWrapper(() -> heapstatsValue.valueProperty().set(new JMXHelper(jmxServiceURL)))); + + jdpTableKeyValue.set(FXCollections.observableArrayList( + new JdpTableKeyValue("Received Time", receivedTime.format(HeapStatsUtils.getDateTimeFormatter())), + new JdpTableKeyValue("Address", sourceAddr.getAddress().getHostAddress()), + new JdpTableKeyValue("JDP Instance Name", instanceName), + new JdpTableKeyValue("Main Class", mainClass), + new JdpTableKeyValue("UUID", uuid.toString()), + new JdpTableKeyValue("PID", Integer.toString(pid)), + new JdpTableKeyValue("JMX URL", jmxURL), + heapstatsValue + )); + jdpList.getItems().add(this); + } + else{ + JdpDecoder existData = jdpList.getItems().get(idx); + existData.receivedTime = receivedTime; + existData.jdpTableKeyValue.get().forEach(p -> setJdpTableKeyValue(p, jmxURL)); + } + + } + + @Override + protected Void call() throws Exception { + Map jdpData = parseJdpPacket(); + + this.mainClass = jdpData.get(MAIN_CLASS_KEY); + this.instanceName = jdpData.get(INSTANCE_NAME_KEY); + this.jmxServiceURL = jdpData.get(JMX_SERVICE_URL_KEY); + this.uuid = UUID.fromString(jdpData.get(DISCOVERABLE_SESSION_UUID_KEY)); + this.pid = Integer.parseInt(jdpData.get(PROCESS_ID_KEY)); + this.broadcastInterval = Integer.parseInt(jdpData.get(PACKET_BROADCAST_INTERVAL_KEY)); + + Platform.runLater(this::updateJDPData); + + return null; + } + + /** + * Get time of this JDP packet was received. + * + * @return Received time. + */ + public LocalDateTime getReceivedTime() { + return receivedTime; + } + + /** + * Get source address of JDP packet. + * + * @return Source address of JDP. + */ + public InetSocketAddress getSourceAddr() { + return sourceAddr; + } + + /** + * Get main class in JDP packet. + * + * @return Main class. + */ + public String getMainClass() { + return mainClass; + } + + /** + * Get JMX URL in JDP packet. + * + * @return JDP URL. + */ + public String getJmxServiceURL() { + return jmxServiceURL; + } + + /** + * Get PID in JDP packet. + * @return PID + */ + public int getPid() { + return pid; + } + + /** + * Get UUID in JDP packet. + * @return UUID + */ + public UUID getUuid() { + return uuid; + } + + /** + * Get instance name in JDP packet. + * @return Instance name. + */ + public String getInstanceName() { + return instanceName; + } + + /** + * Get broadcast interval in JDP packet. + * + * @return JDP broadcast interval. + */ + public int getBroadcastInterval() { + return broadcastInterval; + } + + /** + * Set invalidate to this JDP packet. + */ + public void setInvalidate(){ + invalidate.set(true); + } + + /** + * Get invalidate property. + * + * @return Invalidate property. + */ + public BooleanProperty invalidateProperty(){ + return this.invalidate; + } + + /** + * Get JDP packet data list. + * This method returns key-value list. + * + * @return JDP packet data list. + */ + public ObjectProperty> jdpTableKeyValueProperty(){ + return this.jdpTableKeyValue; + } + + /** + * Get JDP Key-Value. + * + * @return JDP packet data. + */ + public JdpTableKeyValue getHeapStatsTableKeyValue(){ + return this.heapstatsValue; + } + + @Override + public int hashCode() { + return this.uuid.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return this.uuid.equals(((JdpDecoder)obj).uuid); + } + + @Override + public String toString() { + return Optional.ofNullable(instanceName).orElse(mainClass); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpReceiver.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.DatagramChannel; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import javafx.concurrent.Task; +import javafx.scene.control.ListView; +import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; +import jp.co.ntt.oss.heapstats.lambda.PredicateWrapper; + +/** + * JDP Packet receiver thread. + * + * @author Yasumasa Suenaga + */ +public class JdpReceiver extends Task{ + + /** System property key of JDP multicast address. */ + public static final String JDP_ADDRESS_PROP_NAME = "com.sun.management.jdp.address"; + + /** System property key of JDP port, */ + public static final String JDP_PORT_PROP_NAME = "com.sun.management.jdp.port"; + + /** Default JDP multicast address. */ + public static final String JDP_DEFAULT_ADDRESS = "224.0.23.178"; + + /** Default JDP port. */ + public static final int JDP_DEFAULT_PORT = 7095; + + /** UDP packet length. */ + public static final int UDP_PACKET_LENGTH = 65535; // 64KB + + private InetAddress jdpAddr; + + private int jdpPort; + + /** Thread pool for JdpDecoder. */ + private ExecutorService jdpProcPool; + + private ExecutorService jmxProcPool; + + private ListView jdpList; + + private Optional jconsolePath; + + /** + * Constructor of JdpReceiver. + * + * @param jdpAddr JDP multicast address. + * @param jdpPort JDP port. + * @param jdpList ListView which includes JDP packet data. + * @param threadPool ThreadPool which processes JDP packet decording. + * @param jconsolePath Path to JConsole. + * @param jmxPool ThreadPool which processes JMX access. + */ + public JdpReceiver(InetAddress jdpAddr, int jdpPort, ListView jdpList, ExecutorService threadPool, Optional jconsolePath, ExecutorService jmxPool){ + this.jdpAddr = jdpAddr; + this.jdpPort = jdpPort; + this.jdpProcPool = threadPool; + this.jdpList = jdpList; + this.jconsolePath = jconsolePath; + this.jmxProcPool = jmxPool; + } + + /** + * Constructor of JdpReceiver. + * + * @param jdpList ListView which includes JDP packet data. + * @param threadPool ThreadPool which processes JDP packet decording. + * @param jconsolePath Path to JConsole. + * @param jmxPool ThreadPool which processes JMX access. + * + * @throws UnknownHostException Invalid host information to access. + */ + public JdpReceiver(ListView jdpList, ExecutorService threadPool, Optional jconsolePath, ExecutorService jmxPool) throws UnknownHostException{ + this(InetAddress.getByName(Optional.ofNullable(System.getProperty(JDP_ADDRESS_PROP_NAME)).orElse(JDP_DEFAULT_ADDRESS)), + Optional.ofNullable(System.getProperty(JDP_PORT_PROP_NAME)).map(p -> Integer.parseInt(p)).orElse(JDP_DEFAULT_PORT), + jdpList, threadPool, jconsolePath, jmxPool); + } + + @Override + protected Void call() throws Exception { + + try(DatagramChannel jdpChannel = DatagramChannel.open(StandardProtocolFamily.INET)){ + jdpChannel.bind(new InetSocketAddress(jdpPort)); + jdpChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); + + Collections.list(NetworkInterface.getNetworkInterfaces()).stream() + .filter(i -> i.getInterfaceAddresses().stream() + .anyMatch(n -> n.getAddress() instanceof Inet4Address)) + .filter(new PredicateWrapper<>(i -> i.supportsMulticast())) + .peek(new ConsumerWrapper<>(i ->jdpChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, i))) + .forEach(new ConsumerWrapper<>(i -> jdpChannel.join(jdpAddr, i))); + + while(!isCancelled()){ + ByteBuffer buf = ByteBuffer.allocateDirect(UDP_PACKET_LENGTH); + SocketAddress src = jdpChannel.receive(buf); + + buf.flip(); + jdpProcPool.submit(new JdpDecoder((InetSocketAddress)src, buf, this.jdpList, jconsolePath, jmxProcPool)); + } + + } + catch(ClosedByInterruptException ex){ + // Do nothing. + // This exception may be occurred by thread interruption. + } + + return null; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpTableKeyValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpTableKeyValue.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +/** + * JDP key and value which is used in TableView. + * + * @author Yasumasa Suenaga + */ +public class JdpTableKeyValue { + + private final StringProperty key; + + private final ObjectProperty value; + + /** + * Constructor of JdpTableKeyValue. + * + * @param key JDP key. + * @param value JDP value. + */ + public JdpTableKeyValue(String key, Object value){ + this.key = new SimpleStringProperty(key); + this.value = new SimpleObjectProperty<>(value); + } + + /** + * JDP key property. + * @return Key property. + */ + public StringProperty keyProperty(){ + return key; + } + + /** + * JDP value property. + * @return Value property. + */ + public ObjectProperty valueProperty(){ + return value; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpValidatorService.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpValidatorService.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp; + +import java.time.LocalDateTime; +import javafx.application.Platform; +import javafx.concurrent.ScheduledService; +import javafx.concurrent.Task; +import javafx.scene.control.ListView; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.JVMLiveConfig; + +/** + * Validation service class for JdpDecoder. + * This class should be run on ScheduledService. + * + * The task which is provided from this class checkes all JdpDecoder in ListView. + * If JDP Packet is not received until JDP packet interval, the Task change its + * color to orange. + * + * @author Yasumasa Suenaga + */ +public class JdpValidatorService extends ScheduledService{ + + private final ListView jdpList; + + public JdpValidatorService(ListView jdpList) { + this.jdpList = jdpList; + } + + @Override + protected Task createTask() { + return new JdpValidationTask(); + } + + class JdpValidationTask extends Task{ + + private void swapJdpDecoder(int index){ + JdpDecoder target = jdpList.getItems().get(index); + target.setInvalidate(); + + jdpList.getItems().remove(index); + jdpList.getItems().add(index, target); + } + + @Override + protected Void call() throws Exception { + Platform.runLater(() -> { + LocalDateTime now = LocalDateTime.now(); + jdpList.getItems().stream() + .filter(d -> !d.invalidateProperty().get()) + .filter(d -> d.getReceivedTime().plusSeconds(d.getBroadcastInterval() / 1000 + JVMLiveConfig.getJdpWaitDuration()).isBefore(now)) + .mapToInt(d -> jdpList.getItems().indexOf(d)) + .forEach(i -> swapJdpDecoder(i)); + }); + + return null; + } + + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/mbean/HeapStatsConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/mbean/HeapStatsConfig.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.mbean; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.control.Control; + +/** + * Container class of HeapStats agent configuration. + * + * @author Yasumasa Suenaga + */ +public class HeapStatsConfig { + + private final StringProperty key; + + private final Property value; + + private final Object currentValue; + + private Control cellContent; + + private final BooleanProperty changed; + + /** + * Constructor of HeapStatsConfig. + * + * @param key Configuration key. + * @param value Configuration value. + */ + @SuppressWarnings("unchecked") + public HeapStatsConfig(String key, Object value){ + this.key = new SimpleStringProperty(key); + changed = new SimpleBooleanProperty(); + + if(value == null){ + this.value = new SimpleStringProperty(null); + this.currentValue = null; + } + else if(value instanceof Boolean){ + this.value = new SimpleBooleanProperty((Boolean)value); + this.currentValue = (Boolean)value; + } + else if(value instanceof Long){ + this.value = new SimpleLongProperty((Long)value); + this.currentValue = (Long)value; + } + else if(value instanceof String){ + this.value = new SimpleStringProperty((String)value); + this.currentValue = (String)value; + } + else{ + this.value = new SimpleObjectProperty<>(value); + this.currentValue = value; + } + + this.value.addListener((v, o, n) -> changed.set(!n.equals(currentValue))); + this.cellContent = null; + } + + /** + * Get configuration key. + * + * @return Configuration key. + */ + public StringProperty keyProperty(){ + return key; + } + + /** + * Get configuration value. + * + * @return Configuration value. + */ + public Property valueProperty(){ + return value; + } + + /** + * Get change property. + * true if this configuration is changed. + * + * @return Change property. + */ + public BooleanProperty changedProperty(){ + return changed; + } + + /** + * Get table cell control of this configuration. + * + * @return Control in TableCell. + */ + public Control getCellContent() { + return cellContent; + } + + /** + * Set table cell control of this configuration. + * + * @param cellContent New control in TableCell. + */ + public void setCellContent(Control cellContent) { + this.cellContent = cellContent; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.mbean; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutionException; +import javafx.beans.binding.Bindings; +import javafx.beans.property.Property; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Control; +import javafx.scene.control.Label; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.FileChooser; +import javafx.util.converter.IntegerStringConverter; +import javafx.util.converter.LongStringConverter; +import jp.co.ntt.oss.heapstats.fx.MainWindowController; +import jp.co.ntt.oss.heapstats.jmx.JMXHelper; +import jp.co.ntt.oss.heapstats.mbean.HeapStatsMBean; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class of HeapStatsMBean. + * + * @author Yasumasa Suenaga + */ +public class HeapStatsMBeanController implements Initializable { + + @FXML + private Label headerLabel; + + @FXML + TableView configTable; + + @FXML + TableColumn keyColumn; + + @FXML + TableColumn valueColumn; + + private JMXHelper jmxHelper; + + + private static class ConfigKeyTableCell extends TableCell{ + + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + + if(!empty){ + HeapStatsConfig config = (HeapStatsConfig)getTableRow().getItem(); + styleProperty().bind(Bindings.createStringBinding(() -> config.changedProperty().get() ? "-fx-text-fill: orange;" : "-fx-text-fill: black;", config.changedProperty())); + setText(item); + } + + } + + } + + private static class VariousTableCell extends TableCell{ + + @Override + @SuppressWarnings("unchecked") + protected void updateItem(Object item, boolean empty) { + super.updateItem(item, empty); + + if(empty){ + return; + } + + HeapStatsConfig config = (HeapStatsConfig)getTableRow().getItem(); + if(config == null){ + return; + } + + Control cellContent = config.getCellContent(); + if(item instanceof Boolean){ + if(!(cellContent instanceof CheckBox)){ + cellContent = new CheckBox(); + ((CheckBox)cellContent).selectedProperty().bindBidirectional(config.valueProperty()); + } + } + else if(item instanceof HeapStatsMBean.LogLevel){ + if(!(cellContent instanceof ChoiceBox)){ + cellContent = new ChoiceBox(FXCollections.observableArrayList(HeapStatsMBean.LogLevel.values())); + ((ChoiceBox)cellContent).valueProperty().bindBidirectional(config.valueProperty()); + } + } + else if(item instanceof HeapStatsMBean.RankOrder){ + if(!(cellContent instanceof ChoiceBox)){ + cellContent = new ChoiceBox(FXCollections.observableArrayList(HeapStatsMBean.RankOrder.values())); + ((ChoiceBox)cellContent).valueProperty().bindBidirectional(config.valueProperty()); + } + } + else if(item instanceof Integer){ + if(!(cellContent instanceof TextField)){ + cellContent = new TextField(); + ((TextField)cellContent).textProperty().bindBidirectional((Property)config.valueProperty(), new IntegerStringConverter()); + } + } + else if(item instanceof Long){ + if(!(cellContent instanceof TextField)){ + cellContent = new TextField(); + ((TextField)cellContent).textProperty().bindBidirectional((Property)config.valueProperty(), new LongStringConverter()); + } + } + else{ + if(!(cellContent instanceof TextField)){ + cellContent = new TextField(); + ((TextField)cellContent).textProperty().bindBidirectional(config.valueProperty()); + } + } + + config.setCellContent(cellContent); + setGraphic(cellContent); + } + + } + + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + keyColumn.setCellValueFactory(new PropertyValueFactory<>("key")); + keyColumn.setCellFactory(p -> new ConfigKeyTableCell()); + valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); + valueColumn.setCellFactory(p -> new VariousTableCell()); + } + + /** + * Get JMXHelper instance. + * + * @return Instance of JMXHelper. + */ + public JMXHelper getJmxHelper() { + return jmxHelper; + } + + /** + * Set JMXHelper instance. + * + * @param jmxHelper New JMXHelper instance. + */ + public void setJmxHelper(JMXHelper jmxHelper) { + this.jmxHelper = jmxHelper; + } + + /** + * Load all configs from remote HeapStats agent through JMX. + */ + public void loadAllConfigs(){ + headerLabel.setText(jmxHelper.getUrl().toString()); + configTable.setItems(jmxHelper.getMbean().getConfigurationList() + .entrySet() + .stream() + .map(e -> new HeapStatsConfig(e.getKey(), e.getValue())) + .collect(FXCollections::observableArrayList, ObservableList::add, ObservableList::addAll)); + } + + @FXML + private void onCommitBtnClick(ActionEvent event){ + + try{ + configTable.getItems().stream() + .filter(c -> c.changedProperty().get()) + .forEach(c -> jmxHelper.getMbean().changeConfiguration(c.keyProperty().get(), c.valueProperty().getValue())); + } + catch(IllegalArgumentException e){ + HeapStatsUtils.showExceptionDialog(e); + return; + } + + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.applyConfig"), ButtonType.OK); + dialog.show(); + + loadAllConfigs(); + } + + @FXML + private void onInvokeResourceBtnClick(ActionEvent event){ + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + + if(jmxHelper.getMbean().invokeLogCollection()){ + Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.invoke.resource.success"), ButtonType.OK); + dialog.show(); + } + else{ + Alert dialog = new Alert(AlertType.ERROR, resource.getString("dialog.config.message.invoke.resource.fail"), ButtonType.OK); + dialog.show(); + } + } + + @FXML + private void onInvokeAllBtnClick(ActionEvent event){ + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + + if(jmxHelper.getMbean().invokeAllLogCollection()){ + Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.invoke.archive.success"), ButtonType.OK); + dialog.show(); + } + else{ + Alert dialog = new Alert(AlertType.ERROR, resource.getString("dialog.config.message.invoke.archive.fail"), ButtonType.OK); + dialog.show(); + } + } + + @FXML + private void onInvokeSnapShotBtnClick(ActionEvent event){ + jmxHelper.getMbean().invokeSnapShotCollection(); + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.invoke.snapshot"), ButtonType.OK); + dialog.show(); + } + + @FXML + private void onGetResourceBtnClick(ActionEvent event){ + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + dialog.setTitle(resource.getString("dialog.config.resource.save")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("CSV file (*.csv)", "*.csv"), + new FileChooser.ExtensionFilter("All files", "*.*")); + File logFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); + + if(logFile != null){ + try { + jmxHelper.getResourceLog(logFile.toPath()); + } catch (IOException | InterruptedException | ExecutionException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + } + + } + + @FXML + private void onGetSnapShotBtnClick(ActionEvent event){ + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); + dialog.setTitle(resource.getString("dialog.config.snapshot.save")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("SnapShot file (*.dat)", "*.dat"), + new FileChooser.ExtensionFilter("All files", "*.*")); + File snapshotFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); + + if(snapshotFile != null){ + try { + jmxHelper.getSnapShot(snapshotFile.toPath()); + } catch (IOException | InterruptedException | ExecutionException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + } + + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/ArchiveDataConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/ArchiveDataConverter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.log; + +import java.util.Optional; +import javafx.util.StringConverter; +import jp.co.ntt.oss.heapstats.container.log.ArchiveData; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * This class converts LocalDateTime in ArchiveData to String. + * This class DO NOT support fromString() method. + * + * @author Yasumasa Suenaga + */ +public class ArchiveDataConverter extends StringConverter{ + + @Override + public String toString(ArchiveData object) { + return Optional.ofNullable(object) + .map(o -> o.getDate().format(HeapStatsUtils.getDateTimeFormatter())) + .orElse(null); + } + + @Override + public ArchiveData fromString(String string) { + throw new UnsupportedOperationException("ArchiveData DO NOT convert from String."); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/LogController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/LogController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.log; + +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.SplitPane; +import javafx.scene.control.TextField; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import jp.co.ntt.oss.heapstats.fx.MainWindowController; +import jp.co.ntt.oss.heapstats.container.log.ArchiveData; +import jp.co.ntt.oss.heapstats.container.log.DiffData; +import jp.co.ntt.oss.heapstats.container.log.LogData; +import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; +import jp.co.ntt.oss.heapstats.lambda.FunctionWrapper; +import jp.co.ntt.oss.heapstats.api.plugin.PluginController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.tabs.LogDetailsController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.tabs.LogResourcesController; +import jp.co.ntt.oss.heapstats.task.ParseLogFile; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; +import jp.co.ntt.oss.heapstats.fx.utils.TaskAdapter; + +import java.io.File; +import java.net.URL; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.stream.Collectors; + +/** + * FXML Controller of LOG builtin plugin. + * + * @author Yasumasa Suenaga + */ +public class LogController extends PluginController implements Initializable { + + @FXML + private LogResourcesController logResourcesController; + + @FXML + private LogDetailsController logDetailsController; + + @FXML + private Label startTimeLabel; + + @FXML + private Label endTimeLabel; + + @FXML + private TextField logFileList; + + @FXML + private Button okBtn; + + @FXML + private SplitPane rangePane; + + private ObjectProperty rangeStart; + + private ObjectProperty rangeEnd; + + private List logEntries; + + private List diffEntries; + + private ObjectProperty> archiveList; + + /** + * Update caption of label which represents time of selection. + * + * @param target Label compornent to draw. + * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 . + */ + private void updateRangeLabel(Label target, double newValue){ + if(!Optional.ofNullable(logEntries).map(List::isEmpty).orElse(true)){ + LocalDateTime start = logEntries.get(0).getDateTime(); + LocalDateTime end = logEntries.get(logEntries.size() - 1).getDateTime(); + long diff = start.until(end, ChronoUnit.MILLIS); + LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS); + + if(target == startTimeLabel){ + rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS)); + } + else{ + rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS)); + } + + target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter())); + } + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + super.initialize(url, rb); + + logEntries = null; + diffEntries = null; + rangeStart = new SimpleObjectProperty<>(); + rangeEnd = new SimpleObjectProperty<>(); + + rangePane.getDividers().get(0).positionProperty().addListener((v, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue())); + rangePane.getDividers().get(1).positionProperty().addListener((v, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue())); + + archiveList = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); + logResourcesController.archiveListProperty().bind(archiveList); + logDetailsController.archiveListProperty().bind(archiveList); + setOnWindowResize((v, o, n) -> Platform.runLater(logResourcesController::drawEventLineToChart)); + } + + /** + * onSucceeded event handler for LogFileParser. + * + * @param parser Targeted LogFileParser. + */ + private void onLogFileParserSucceeded(ParseLogFile parser) { + logEntries = parser.getLogEntries(); + diffEntries = parser.getDiffEntries(); + + rangePane.getDividers().get(0).setPosition(0.0d); + rangePane.getDividers().get(1).setPosition(1.0d); + + rangePane.setDisable(false); + okBtn.setDisable(false); + } + + /** + * Event handler of LogFile button. + * + * @param event ActionEvent of this event. + */ + @FXML + public void onLogFileClick(ActionEvent event) { + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("logResources", new Locale(HeapStatsUtils.getLanguage())); + dialog.setTitle(resource.getString("dialog.filechooser.title")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new ExtensionFilter("Log file (*.csv)", "*.csv"), + new ExtensionFilter("All files", "*.*")); + + List logList = dialog.showOpenMultipleDialog(MainWindowController.getInstance().getOwner()); + + if (logList != null) { + logResourcesController.clearAllItems(); + logDetailsController.clearAllItems(); + + HeapStatsUtils.setDefaultDirectory(logList.get(0).getParent()); + String logListStr = logList.stream() + .map(File::getAbsolutePath) + .collect(Collectors.joining("; ")); + + logFileList.setText(logListStr); + + TaskAdapter task = new TaskAdapter<>(new ParseLogFile(logList, true)); + task.setOnSucceeded(evt -> onLogFileParserSucceeded(task.getTask())); + super.bindTask(task); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + + } + + /** + * Event handler of OK button. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onOkClick(ActionEvent event) { + /* Get range */ + LocalDateTime start = rangeStart.getValue(); + LocalDateTime end = rangeEnd.getValue(); + + List targetLogData = logEntries.parallelStream() + .filter(d -> ((d.getDateTime().compareTo(start) >= 0) && (d.getDateTime().compareTo(end) <= 0))) + .collect(Collectors.toList()); + List targetDiffData = diffEntries.parallelStream() + .filter(d -> ((d.getDateTime().compareTo(start) >= 0) && (d.getDateTime().compareTo(end) <= 0))) + .collect(Collectors.toList()); + + Task task = logResourcesController.createDrawResourceCharts(targetLogData, targetDiffData); + super.bindTask(task); + Thread drawChartThread = new Thread(task); + drawChartThread.start(); + + archiveList.set(FXCollections.observableArrayList(targetLogData.stream() + .filter(d -> d.getArchivePath() != null) + .map(new FunctionWrapper<>(ArchiveData::new)) + .peek(new ConsumerWrapper<>(a -> a.parseArchive())) + .collect(Collectors.toList()))); + } + + /** + * Returns plugin name. This value is used to show in main window tab. + * + * @return Plugin name. + */ + @Override + public String getPluginName() { + return "Log Data"; + } + + @Override + public EventHandler getOnPluginTabSelected() { + return null; + } + + @Override + public String getLicense() { + return PluginController.LICENSE_GPL_V2; + } + + @Override + public Map getLibraryLicense() { + return null; + } + + @Override + public Runnable getOnCloseRequest() { + return null; + } + + @Override + public void setData(Object data, boolean select) { + super.setData(data, select); + logFileList.setText((String) data); + + TaskAdapter task = new TaskAdapter<>(new ParseLogFile(Arrays.asList(new File((String) data)), true)); + task.setOnSucceeded(evt -> onLogFileParserSucceeded(task.getTask())); + super.bindTask(task); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/LogDataConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/LogDataConverter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.log; + +import javafx.util.StringConverter; +import jp.co.ntt.oss.heapstats.container.log.LogData; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * StringConverter for LogData. This class provides method to convert + * LocalDateTime in LogData to String. + * + * @author Yasumasa Suenaga. + */ + +public class LogDataConverter extends StringConverter { + + @Override + public String toString(LogData object) { + return object.getDateTime().format(HeapStatsUtils.getDateTimeFormatter()); + } + + /** + * This class DO NOT support this method. + * + * @param string String + * @return UnsupportedOperationException This class cannot convert from string. + */ + @Override + public LogData fromString(String string) { + throw new UnsupportedOperationException("LogData DO NOT convert from String."); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/tabs/LogDetailsController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/tabs/LogDetailsController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015-2019 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.tabs; + +import java.io.IOException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.Map; +import java.util.ResourceBundle; +import javafx.beans.property.ObjectProperty; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextArea; +import javafx.scene.control.cell.PropertyValueFactory; +import jp.co.ntt.oss.heapstats.container.log.ArchiveData; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.ArchiveDataConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class for "Log Detail Data" tab in LogData plugin. + */ +public class LogDetailsController implements Initializable { + + @FXML + private TableView> archiveEnvInfoTable; + + @FXML + private TableColumn, String> archiveKeyColumn; + + @FXML + private TableColumn, String> archiveVauleColumn; + + @FXML + private ComboBox fileCombo; + + @FXML + private ComboBox archiveCombo; + + @FXML + private TextArea logArea; + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + archiveCombo.setConverter(new ArchiveDataConverter()); + archiveKeyColumn.setCellValueFactory(new PropertyValueFactory<>("key")); + archiveVauleColumn.setCellValueFactory(new PropertyValueFactory<>("value")); + } + + /** + * Event handler of archive combobox. This handler is fired that user select + * archive. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onArchiveComboAction(ActionEvent event) { + archiveEnvInfoTable.getItems().clear(); + fileCombo.getItems().clear(); + ArchiveData target = archiveCombo.getValue(); + + if (target == null) { + return; + } + + /* + * Convert Map to List. + * Map.Entry of HashMap (HashMap$Node) is package private class. So JavaFX + * cannot access them through reflection API. + * Thus I convert Map.Entry to AbstractMap.SimpleEntry. + */ + target.getEnvInfo().entrySet().forEach(e -> archiveEnvInfoTable.getItems().add(new AbstractMap.SimpleEntry<>(e))); + + fileCombo.getItems().addAll(target.getFileList()); + } + + /** + * Event handler of selecting log in this archive. This handler is fired + * that user select log. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onFileComboAction(ActionEvent event) { + ArchiveData target = archiveCombo.getValue(); + String file = fileCombo.getValue(); + + if((target == null) || (file == null)){ + return; + } + + try { + logArea.setText(target.getFileContents(file)); + } catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + logArea.setText(""); + } + + } + + /** + * Get HeapStats ZIP archive list as Property. + * + * @return List of HeapStats ZIP archive. + */ + public ObjectProperty> archiveListProperty() { + return archiveCombo.itemsProperty(); + } + + /** + * Clear all items in Log Details tab. + */ + public void clearAllItems(){ + fileCombo.getItems().clear(); + logArea.setText(""); + archiveEnvInfoTable.getItems().clear(); +} + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/tabs/LogResourcesController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/log/tabs/LogResourcesController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,807 @@ +/* + * Copyright (C) 2015-2019 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.tabs; + +import java.net.URL; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; +import java.util.ResourceBundle; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.chart.Axis; +import javafx.scene.chart.LineChart; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.StackedAreaChart; +import javafx.scene.chart.XYChart; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.Path; +import javafx.scene.shape.Rectangle; +import jp.co.ntt.oss.heapstats.container.log.ArchiveData; +import jp.co.ntt.oss.heapstats.container.log.DiffData; +import jp.co.ntt.oss.heapstats.container.log.LogData; +import jp.co.ntt.oss.heapstats.container.log.SummaryData; +import jp.co.ntt.oss.heapstats.fx.utils.EpochTimeConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class for "Resource Data" tab in LogData plugin. + */ +public class LogResourcesController implements Initializable { + + @FXML + private GridPane chartGrid; + + @FXML + private StackedAreaChart javaCPUChart; + + private XYChart.Series javaUserUsage; + + private XYChart.Series javaSysUsage; + + @FXML + private StackedAreaChart systemCPUChart; + + private XYChart.Series systemUserUsage; + + private XYChart.Series systemNiceUsage; + + private XYChart.Series systemSysUsage; + + private XYChart.Series systemIdleUsage; + + private XYChart.Series systemIOWaitUsage; + + private XYChart.Series systemIRQUsage; + + private XYChart.Series systemSoftIRQUsage; + + private XYChart.Series systemStealUsage; + + private XYChart.Series systemGuestUsage; + + @FXML + private LineChart javaMemoryChart; + + private XYChart.Series javaVSZUsage; + + private XYChart.Series javaRSSUsage; + + @FXML + private LineChart safepointChart; + + private XYChart.Series safepoints; + + @FXML + private LineChart safepointTimeChart; + + private XYChart.Series safepointTime; + + @FXML + private LineChart threadChart; + + private XYChart.Series threads; + + @FXML + private LineChart monitorChart; + + private XYChart.Series monitors; + + @FXML + private TableView procSummary; + + @FXML + private TableColumn categoryColumn; + + @FXML + private TableColumn valueColumn; + + private ObjectProperty> archiveList; + + private List suspectList; + + private ResourceBundle resource; + + private EpochTimeConverter epochTimeConverter; + + /* Tooltip for Java CPU chart */ + private Tooltip javaCPUTooltip; + + private GridPane javaCPUTooltipGrid; + + private Label javaUserLabel; + + private Label javaSysLabel; + + /* Tooltip for System CPU chart */ + private Tooltip systemCPUTooltip; + + private GridPane systemCPUTooltipGrid; + + private Label systemUserLabel; + + private Label systemNiceLabel; + + private Label systemSysLabel; + + private Label systemIdleLabel; + + private Label systemIOWaitLabel; + + private Label systemIRQLabel; + + private Label systemSoftIRQLabel; + + private Label systemStealLabel; + + private Label systemGuestLabel; + + /* Tooltip for Java Memory chart */ + private Tooltip javaMemoryTooltip; + + private GridPane javaMemoryTooltipGrid; + + private Label javaMemoryVSZLabel; + + private Label javaMemoryRSSLabel; + + /* Generic Tooltip */ + private Tooltip tooltip; + + private void initializeJavaCPUTooltip(){ + javaUserLabel = new Label(); + javaSysLabel = new Label(); + + Rectangle javaUserRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle javaSysRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + javaUserRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + javaSysRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + }); + + javaCPUTooltipGrid = new GridPane(); + javaCPUTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + javaCPUTooltipGrid.add(javaUserRect, 0, 0); + javaCPUTooltipGrid.add(new Label("user"), 1, 0); + javaCPUTooltipGrid.add(javaUserLabel, 2, 0); + javaCPUTooltipGrid.add(javaSysRect, 0, 1); + javaCPUTooltipGrid.add(new Label("sys"), 1, 1); + javaCPUTooltipGrid.add(javaSysLabel, 2, 1); + + javaCPUTooltip = new Tooltip(); + javaCPUTooltip.setGraphic(javaCPUTooltipGrid); + javaCPUTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + + private void initializeJavaMemoryTooltip(){ + javaMemoryVSZLabel = new Label(); + javaMemoryRSSLabel = new Label(); + + Rectangle javaMemoryVSZRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle javaMemoryRSSRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + javaMemoryRSSRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + javaMemoryVSZRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + }); + + javaMemoryTooltipGrid = new GridPane(); + javaMemoryTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + javaMemoryTooltipGrid.add(javaMemoryVSZRect, 0, 0); + javaMemoryTooltipGrid.add(new Label("VSZ"), 1, 0); + javaMemoryTooltipGrid.add(javaMemoryVSZLabel, 2, 0); + javaMemoryTooltipGrid.add(javaMemoryRSSRect, 0, 1); + javaMemoryTooltipGrid.add(new Label("RSS"), 1, 1); + javaMemoryTooltipGrid.add(javaMemoryRSSLabel, 2, 1); + + javaMemoryTooltip = new Tooltip(); + javaMemoryTooltip.setGraphic(javaMemoryTooltipGrid); + javaMemoryTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + + private void initializeSystemCPUTooltip(){ + systemUserLabel = new Label(); + systemNiceLabel = new Label(); + systemSysLabel = new Label(); + systemIdleLabel = new Label(); + systemIOWaitLabel = new Label(); + systemIRQLabel = new Label(); + systemSoftIRQLabel = new Label(); + systemStealLabel = new Label(); + systemGuestLabel = new Label(); + + Rectangle systemUserRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemNiceRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemSysRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemIdleRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemIOWaitRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemIRQRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemSoftIRQRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemStealRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle systemGuestRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + systemUserRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + systemNiceRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + systemSysRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series2")).getFill().toString().replace("0x", "#")); + systemIdleRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series3")).getFill().toString().replace("0x", "#")); + systemIOWaitRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series4")).getFill().toString().replace("0x", "#")); + systemIRQRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series5")).getFill().toString().replace("0x", "#")); + systemSoftIRQRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series6")).getFill().toString().replace("0x", "#")); + systemStealRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series7")).getFill().toString().replace("0x", "#")); + systemGuestRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series8")).getFill().toString().replace("0x", "#")); + }); + + systemCPUTooltipGrid = new GridPane(); + systemCPUTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + systemCPUTooltipGrid.add(systemUserRect, 0, 0); + systemCPUTooltipGrid.add(new Label("user"), 1, 0); + systemCPUTooltipGrid.add(systemUserLabel, 2, 0); + systemCPUTooltipGrid.add(systemNiceRect, 0, 1); + systemCPUTooltipGrid.add(new Label("nice"), 1, 1); + systemCPUTooltipGrid.add(systemNiceLabel, 2, 1); + systemCPUTooltipGrid.add(systemSysRect, 0, 2); + systemCPUTooltipGrid.add(new Label("sys"), 1, 2); + systemCPUTooltipGrid.add(systemSysLabel, 2, 2); + systemCPUTooltipGrid.add(systemIdleRect, 0, 3); + systemCPUTooltipGrid.add(new Label("idle"), 1, 3); + systemCPUTooltipGrid.add(systemIdleLabel, 2, 3); + systemCPUTooltipGrid.add(systemIOWaitRect, 0, 4); + systemCPUTooltipGrid.add(new Label("iowait"), 1, 4); + systemCPUTooltipGrid.add(systemIOWaitLabel, 2, 4); + systemCPUTooltipGrid.add(systemIRQRect, 0, 5); + systemCPUTooltipGrid.add(new Label("IRQ"), 1, 5); + systemCPUTooltipGrid.add(systemIRQLabel, 2, 5); + systemCPUTooltipGrid.add(systemSoftIRQRect, 0, 6); + systemCPUTooltipGrid.add(new Label("Soft IRQ"), 1, 6); + systemCPUTooltipGrid.add(systemSoftIRQLabel, 2, 6); + systemCPUTooltipGrid.add(systemStealRect, 0, 7); + systemCPUTooltipGrid.add(new Label("steal"), 1, 7); + systemCPUTooltipGrid.add(systemStealLabel, 2, 7); + systemCPUTooltipGrid.add(systemGuestRect, 0, 8); + systemCPUTooltipGrid.add(new Label("guest"), 1, 8); + systemCPUTooltipGrid.add(systemGuestLabel, 2, 8); + + systemCPUTooltip = new Tooltip(); + systemCPUTooltip.setGraphic(systemCPUTooltipGrid); + systemCPUTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + resource = rb; + + categoryColumn.setCellValueFactory(new PropertyValueFactory<>("category")); + valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); + + String bgcolor = "-fx-background-color: " + HeapStatsUtils.getChartBgColor() + ";"; + Stream.of(javaCPUChart, systemCPUChart, javaMemoryChart, safepointChart, safepointTimeChart, threadChart, monitorChart) + .peek(c -> c.lookup(".chart").setStyle(bgcolor)) + .forEach(c -> c.getXAxis().setTickMarkVisible(HeapStatsUtils.getTickMarkerSwitch())); + + initializeChartSeries(); + + archiveList = new SimpleObjectProperty<>(); + epochTimeConverter = new EpochTimeConverter(); + + initializeJavaCPUTooltip(); + initializeSystemCPUTooltip(); + initializeJavaMemoryTooltip(); + } + + /** + * Initialize Series in Chart. This method uses to avoid RuntimeException + * which is related to: RT-37994: [FXML] ProxyBuilder does not support + * read-only collections https://javafx-jira.kenai.com/browse/RT-37994 + */ + @SuppressWarnings("unchecked") + private void initializeChartSeries() { + threads = new XYChart.Series<>(); + threads.setName("Threads"); + threadChart.getData().add(threads); + + javaUserUsage = new XYChart.Series<>(); + javaUserUsage.setName("user"); + javaSysUsage = new XYChart.Series<>(); + javaSysUsage.setName("sys"); + javaCPUChart.getData().addAll(javaUserUsage, javaSysUsage); + + systemUserUsage = new XYChart.Series<>(); + systemUserUsage.setName("user"); + systemNiceUsage = new XYChart.Series<>(); + systemNiceUsage.setName("nice"); + systemSysUsage = new XYChart.Series<>(); + systemSysUsage.setName("sys"); + systemIdleUsage = new XYChart.Series<>(); + systemIdleUsage.setName("idle"); + systemIOWaitUsage = new XYChart.Series<>(); + systemIOWaitUsage.setName("I/O wait"); + systemIRQUsage = new XYChart.Series<>(); + systemIRQUsage.setName("IRQ"); + systemSoftIRQUsage = new XYChart.Series<>(); + systemSoftIRQUsage.setName("soft IRQ"); + systemStealUsage = new XYChart.Series<>(); + systemStealUsage.setName("steal"); + systemGuestUsage = new XYChart.Series<>(); + systemGuestUsage.setName("guest"); + systemCPUChart.getData().addAll(systemUserUsage, systemNiceUsage, systemSysUsage, + systemIdleUsage, systemIOWaitUsage, systemIRQUsage, + systemSoftIRQUsage, systemStealUsage, systemGuestUsage); + + javaVSZUsage = new XYChart.Series<>(); + javaVSZUsage.setName("VSZ"); + javaRSSUsage = new XYChart.Series<>(); + javaRSSUsage.setName("RSS"); + javaMemoryChart.getData().addAll(javaVSZUsage, javaRSSUsage); + + safepoints = new XYChart.Series<>(); + safepoints.setName("Safepoints"); + safepointChart.getData().add(safepoints); + + safepointTime = new XYChart.Series<>(); + safepointTime.setName("Safepoint Time"); + safepointTimeChart.getData().add(safepointTime); + + monitors = new XYChart.Series<>(); + monitors.setName("Monitors"); + monitorChart.getData().add(monitors); + } + + private void drawLineInternal(StackPane target, List drawList, String style) { + AnchorPane anchor = null; + XYChart chart = null; + + for (Node node : ((StackPane) target).getChildren()) { + + if (node instanceof AnchorPane) { + anchor = (AnchorPane) node; + } else if (node instanceof XYChart) { + chart = (XYChart) node; + } + + if ((anchor != null) && (chart != null)) { + break; + } + + } + + if ((anchor == null) || (chart == null)) { + throw new IllegalStateException(resource.getString("message.drawline")); + } + + ObservableList anchorChildren = anchor.getChildren(); + anchorChildren.removeAll(anchorChildren.stream() + .filter(n -> n instanceof Rectangle) + .map(n -> (Rectangle) n) + .filter(r -> r.getStyle().equals(style)) + .collect(Collectors.toList())); + + NumberAxis xAxis = (NumberAxis) chart.getXAxis(); + Axis yAxis = chart.getYAxis(); + Label chartTitle = (Label) chart.getChildrenUnmodifiable().stream() + .filter(n -> n.getStyleClass().contains("chart-title")) + .findFirst() + .get(); + + double startX = xAxis.getLayoutX() + 4.0d; + double yPos = yAxis.getLayoutY() + chartTitle.getLayoutY() + chartTitle.getHeight(); + List rectList = drawList.stream() + .map(t -> new Rectangle(xAxis.getDisplayPosition(t) + startX, yPos, 2.0d, yAxis.getHeight())) + .peek(r -> ((Rectangle) r).setStyle(style)) + .collect(Collectors.toList()); + anchorChildren.addAll(rectList); + } + + private void drawArchiveLine() { + + if (archiveList.get().isEmpty()) { + return; + } + + List archiveDateList = archiveList.get() + .stream() + .map(a -> a.getDate().atZone(ZoneId.systemDefault()).toEpochSecond()) + .collect(Collectors.toList()); + chartGrid.getChildren().stream() + .filter(n -> n instanceof StackPane) + .forEach(p -> drawLineInternal((StackPane) p, archiveDateList, "-fx-fill: black;")); + } + + /** + * Draw line which represents to suspect to reboot. This method does not + * clear AnchorPane to draw lines. So this method must be called after + * drawArchiveLine(). + */ + private void drawRebootSuspectLine() { + + if ((suspectList == null) || suspectList.isEmpty()) { + return; + } + + List suspectRebootDateList = suspectList.stream() + .map(d -> d.atZone(ZoneId.systemDefault()).toEpochSecond()) + .collect(Collectors.toList()); + chartGrid.getChildren().stream() + .filter(n -> n instanceof StackPane) + .forEach(p -> drawLineInternal((StackPane) p, suspectRebootDateList, "-fx-fill: yellow;")); + } + + /** + * Draw lines which represents reboot and ZIP archive timing. + */ + public void drawEventLineToChart() { + drawArchiveLine(); + drawRebootSuspectLine(); + } + + /** + * Task class for drawing log chart data. + */ + private class DrawLogChartTask extends Task { + + /* Java CPU */ + private final ObservableList> javaUserUsageBuf; + private final ObservableList> javaSysUsageBuf; + + /* System CPU */ + private final ObservableList> systemUserUsageBuf; + private final ObservableList> systemNiceUsageBuf; + private final ObservableList> systemSysUsageBuf; + private final ObservableList> systemIdleUsageBuf; + private final ObservableList> systemIOWaitUsageBuf; + private final ObservableList> systemIRQUsageBuf; + private final ObservableList> systemSoftIRQUsageBuf; + private final ObservableList> systemStealUsageBuf; + private final ObservableList> systemGuestUsageBuf; + + /* Java Memory */ + private final ObservableList> javaVSZUsageBuf; + private final ObservableList> javaRSSUsageBuf; + + /* Safepoints */ + private final ObservableList> safepointsBuf; + private final ObservableList> safepointTimeBuf; + + /* Threads */ + private final ObservableList> threadsBuf; + + /* Monitor contantion */ + private final ObservableList> monitorsBuf; + + private final List targetLogData; + + private final List targetDiffData; + + private long loopCount; + + private final long totalLoopCount; + + public DrawLogChartTask(List targetLogData, List targetDiffData) { + javaUserUsageBuf = FXCollections.observableArrayList(); + javaSysUsageBuf = FXCollections.observableArrayList(); + systemUserUsageBuf = FXCollections.observableArrayList(); + systemNiceUsageBuf = FXCollections.observableArrayList(); + systemSysUsageBuf = FXCollections.observableArrayList(); + systemIdleUsageBuf = FXCollections.observableArrayList(); + systemIOWaitUsageBuf = FXCollections.observableArrayList(); + systemIRQUsageBuf = FXCollections.observableArrayList(); + systemSoftIRQUsageBuf = FXCollections.observableArrayList(); + systemStealUsageBuf = FXCollections.observableArrayList(); + systemGuestUsageBuf = FXCollections.observableArrayList(); + javaVSZUsageBuf = FXCollections.observableArrayList(); + javaRSSUsageBuf = FXCollections.observableArrayList(); + safepointsBuf = FXCollections.observableArrayList(); + safepointTimeBuf = FXCollections.observableArrayList(); + threadsBuf = FXCollections.observableArrayList(); + monitorsBuf = FXCollections.observableArrayList(); + + this.targetLogData = targetLogData; + this.targetDiffData = targetDiffData; + totalLoopCount = targetDiffData.size() + targetLogData.size(); + } + + private void addDiffData(DiffData data) { + long time = data.getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); + + javaUserUsageBuf.add(new XYChart.Data<>(time, data.getJavaUserUsage())); + javaSysUsageBuf.add(new XYChart.Data<>(time, data.getJavaSysUsage())); + systemUserUsageBuf.add(new XYChart.Data<>(time, data.getCpuUserUsage())); + systemNiceUsageBuf.add(new XYChart.Data<>(time, data.getCpuNiceUsage())); + systemSysUsageBuf.add(new XYChart.Data<>(time, data.getCpuSysUsage())); + systemIdleUsageBuf.add(new XYChart.Data<>(time, data.getCpuIdleUsage())); + systemIOWaitUsageBuf.add(new XYChart.Data<>(time, data.getCpuIOWaitUsage())); + systemIRQUsageBuf.add(new XYChart.Data<>(time, data.getCpuIRQUsage())); + systemSoftIRQUsageBuf.add(new XYChart.Data<>(time, data.getCpuSoftIRQUsage())); + systemStealUsageBuf.add(new XYChart.Data<>(time, data.getCpuStealUsage())); + systemGuestUsageBuf.add(new XYChart.Data<>(time, data.getCpuGuestUsage())); + monitorsBuf.add(new XYChart.Data<>(time, data.getJvmSyncPark())); + safepointsBuf.add(new XYChart.Data<>(time, data.getJvmSafepoints())); + safepointTimeBuf.add(new XYChart.Data<>(time, data.getJvmSafepointTime())); + + updateProgress(); + } + + private void addLogData(LogData data) { + long time = data.getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); + + javaVSZUsageBuf.add(new XYChart.Data<>(time, data.getJavaVSSize() / 1024 / 1024)); + javaRSSUsageBuf.add(new XYChart.Data<>(time, data.getJavaRSSize() / 1024 / 1024)); + threadsBuf.add(new XYChart.Data<>(time, data.getJvmLiveThreads())); + + updateProgress(); + } + + private void setJavaCPUChartTooltip(int idx){ + XYChart.Data userNode = javaUserUsage.getData().get(idx); + XYChart.Data sysNode = javaSysUsage.getData().get(idx); + + EventHandler handler = e -> { + javaCPUTooltip.setText(epochTimeConverter.toString(userNode.getXValue())); + javaUserLabel.setText(String.format("%.02f", userNode.getYValue()) + " %"); + javaSysLabel.setText(String.format("%.02f", sysNode.getYValue()) + " %"); + }; + + Tooltip.install(userNode.getNode(), javaCPUTooltip); + userNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(sysNode.getNode(), javaCPUTooltip); + sysNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setJavaMemoryChartTooltip(int idx){ + XYChart.Data vszNode = javaVSZUsage.getData().get(idx); + XYChart.Data rssNode = javaRSSUsage.getData().get(idx); + + EventHandler handler = e -> { + javaMemoryTooltip.setText(epochTimeConverter.toString(vszNode.getXValue())); + javaMemoryVSZLabel.setText(vszNode.getYValue() + " MB"); + javaMemoryRSSLabel.setText(rssNode.getYValue() + " MB"); + }; + + Tooltip.install(vszNode.getNode(), javaMemoryTooltip); + vszNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(rssNode.getNode(), javaMemoryTooltip); + rssNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setSystemCPUChartTooltip(int idx){ + XYChart.Data userNode = systemUserUsage.getData().get(idx); + XYChart.Data niceNode = systemNiceUsage.getData().get(idx); + XYChart.Data sysNode = systemSysUsage.getData().get(idx); + XYChart.Data idleNode = systemIdleUsage.getData().get(idx); + XYChart.Data iowaitNode = systemIOWaitUsage.getData().get(idx); + XYChart.Data irqNode = systemIRQUsage.getData().get(idx); + XYChart.Data softIrqNode = systemSoftIRQUsage.getData().get(idx); + XYChart.Data stealNode = systemStealUsage.getData().get(idx); + XYChart.Data guestNode = systemGuestUsage.getData().get(idx); + + EventHandler handler = e -> { + systemCPUTooltip.setText(epochTimeConverter.toString(userNode.getXValue())); + systemUserLabel.setText(String.format("%.02f", userNode.getYValue()) + " %"); + systemNiceLabel.setText(String.format("%.02f", niceNode.getYValue()) + " %"); + systemSysLabel.setText(String.format("%.02f", sysNode.getYValue()) + " %"); + systemIdleLabel.setText(String.format("%.02f", idleNode.getYValue()) + " %"); + systemIOWaitLabel.setText(String.format("%.02f", iowaitNode.getYValue()) + " %"); + systemIRQLabel.setText(String.format("%.02f", irqNode.getYValue()) + " %"); + systemSoftIRQLabel.setText(String.format("%.02f", softIrqNode.getYValue()) + " %"); + systemStealLabel.setText(String.format("%.02f", stealNode.getYValue()) + " %"); + systemGuestLabel.setText(String.format("%.02f", guestNode.getYValue()) + " %"); + }; + + Tooltip.install(userNode.getNode(), systemCPUTooltip); + userNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(niceNode.getNode(), systemCPUTooltip); + niceNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(sysNode.getNode(), systemCPUTooltip); + sysNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(idleNode.getNode(), systemCPUTooltip); + idleNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(iowaitNode.getNode(), systemCPUTooltip); + iowaitNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(irqNode.getNode(), systemCPUTooltip); + irqNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(softIrqNode.getNode(), systemCPUTooltip); + softIrqNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(stealNode.getNode(), systemCPUTooltip); + stealNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(guestNode.getNode(), systemCPUTooltip); + guestNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setChartData() { + + if(targetLogData.isEmpty() || targetDiffData.isEmpty()){ + Stream.of(javaMemoryChart, threadChart) + .flatMap(c -> c.getData().stream()) + .forEach(s -> s.getData().clear()); + Stream.of(javaCPUChart, systemCPUChart, safepointChart, safepointTimeChart, monitorChart) + .flatMap(c -> c.getData().stream()) + .forEach(s -> s.getData().clear()); + procSummary.getItems().clear(); + suspectList = null; + return; + } + + /* Set chart range */ + long startLogEpoch = targetLogData.get(0).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); + long endLogEpoch = targetLogData.get(targetLogData.size() - 1).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); + long startDiffEpoch = targetDiffData.get(0).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); + long endDiffEpoch = targetDiffData.get(targetDiffData.size() - 1).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); + Stream.of(javaMemoryChart, threadChart) + .map(c -> (NumberAxis)c.getXAxis()) + .peek(a -> a.setTickUnit((endLogEpoch - startLogEpoch) / HeapStatsUtils.getXTickUnit())) + .peek(a -> a.setLowerBound(startLogEpoch)) + .forEach(a -> a.setUpperBound(endLogEpoch)); + Stream.of(javaCPUChart, systemCPUChart, safepointChart, safepointTimeChart, monitorChart) + .map(c -> (NumberAxis)c.getXAxis()) + .peek(a -> a.setTickUnit((endDiffEpoch - startDiffEpoch) / HeapStatsUtils.getXTickUnit())) + .peek(a -> a.setLowerBound(startDiffEpoch)) + .forEach(a -> a.setUpperBound(endDiffEpoch)); + + /* Replace new chart data */ + javaUserUsage.setData(javaUserUsageBuf); + javaSysUsage.setData(javaSysUsageBuf); + + systemUserUsage.setData(systemUserUsageBuf); + systemNiceUsage.setData(systemNiceUsageBuf); + systemSysUsage.setData(systemSysUsageBuf); + systemIdleUsage.setData(systemIdleUsageBuf); + systemIOWaitUsage.setData(systemIOWaitUsageBuf); + systemIRQUsage.setData(systemIRQUsageBuf); + systemSoftIRQUsage.setData(systemSoftIRQUsageBuf); + systemStealUsage.setData(systemStealUsageBuf); + systemGuestUsage.setData(systemGuestUsageBuf); + + monitors.setData(monitorsBuf); + + safepoints.setData(safepointsBuf); + safepointTime.setData(safepointTimeBuf); + + javaVSZUsage.setData(javaVSZUsageBuf); + javaRSSUsage.setData(javaRSSUsageBuf); + + threads.setData(threadsBuf); + + /* Tooltip setting */ + IntStream.range(0, targetDiffData.size()) + .peek(this::setJavaCPUChartTooltip) + .forEach(this::setSystemCPUChartTooltip); + IntStream.range(0, targetLogData.size()) + .forEach(this::setJavaMemoryChartTooltip); + Tooltip tooltip = new Tooltip(); + Stream.of(threads, safepoints, monitors) + .flatMap(c -> c.getData().stream()) + .peek(d -> Tooltip.install(d.getNode(), tooltip)) + .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText((epochTimeConverter.toString(d.getXValue()) + ": " + d.getYValue())))); + safepointTime.getData() + .stream() + .peek(d -> Tooltip.install(d.getNode(), tooltip)) + .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText(String.format("%s: %d ms", epochTimeConverter.toString(d.getXValue()), d.getYValue())))); + + /* Put summary data to table */ + SummaryData summary = new SummaryData(targetLogData, targetDiffData); + procSummary.setItems(FXCollections.observableArrayList(new SummaryData.SummaryDataEntry(resource.getString("summary.cpu.average"), String.format("%.1f %%", summary.getAverageCPUUsage())), + new SummaryData.SummaryDataEntry(resource.getString("summary.cpu.peak"), String.format("%.1f %%", summary.getMaxCPUUsage())), + new SummaryData.SummaryDataEntry(resource.getString("summary.vsz.average"), String.format("%.1f MB", summary.getAverageVSZ())), + new SummaryData.SummaryDataEntry(resource.getString("summary.vsz.peak"), String.format("%.1f MB", summary.getMaxVSZ())), + new SummaryData.SummaryDataEntry(resource.getString("summary.rss.average"), String.format("%.1f MB", summary.getAverageRSS())), + new SummaryData.SummaryDataEntry(resource.getString("summary.rss.peak"), String.format("%.1f MB", summary.getMaxRSS())), + new SummaryData.SummaryDataEntry(resource.getString("summary.threads.average"), String.format("%.1f", summary.getAverageLiveThreads())), + new SummaryData.SummaryDataEntry(resource.getString("summary.threads.peak"), Long.toString(summary.getMaxLiveThreads())) + )); + + /* + * drawArchiveLine() needs positions in each chart. + * So I call it next event. + */ + suspectList = targetDiffData.stream() + .filter(d -> d.hasMinusData()) + .map(d -> d.getDateTime()) + .collect(Collectors.toList()); + + Platform.runLater(() -> drawEventLineToChart()); + } + + private void updateProgress() { + updateProgress(++loopCount, totalLoopCount); + } + + @Override + protected Void call() throws Exception { + loopCount = 0; + + /* Generate graph data */ + targetDiffData.forEach(this::addDiffData); + targetLogData.forEach(this::addLogData); + + Platform.runLater(this::setChartData); + + return null; + } + + } + + /** + * Get Task instance which draws each chart. + * + * @param targetLogData List of LogData to draw. + * @param targetDiffData List of DiffData to draw. + * + * @return Task instance to draw charts. + */ + public Task createDrawResourceCharts(List targetLogData, List targetDiffData) { + return new DrawLogChartTask(targetLogData, targetDiffData); + } + + /** + * Get HeapStats ZIP archive list as Property. + * + * @return List of HeapStats ZIP archive. + */ + public ObjectProperty> archiveListProperty() { + return archiveList; + } + + /** + * Clear all items in Resource Data tab. + */ + @SuppressWarnings({"raw", "unchecked"}) + public void clearAllItems(){ + + for(Node n : chartGrid.getChildren()){ + if(n instanceof StackPane){ + for(Node cn : ((StackPane)n).getChildren()){ + if(cn instanceof AnchorPane){ // Clear archive and reboot suspect lines. + ((AnchorPane)cn).getChildren().clear(); +} + else if(cn instanceof XYChart){ // Clear chart data. + ((XYChart)cn).getData().stream() + .forEach(s -> ((XYChart.Series)s).getData().clear()); + } + } + } + } + + /* Claer summary table. */ + procSummary.getItems().clear(); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/BindingFilter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/BindingFilter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import jp.co.ntt.oss.heapstats.xml.binding.Filter; + +/** + * JAXB binding class for exclude filter. + * This class will be used in JavaFX application. + * + * @author Yasumasa Suenaga + */ +public class BindingFilter extends Filter{ + + private final BooleanProperty hideProp; + + private final BooleanProperty applied; + + public BindingFilter(Filter f){ + setName(f.getName()); + setClasses(f.getClasses()); + hideProp = new SimpleBooleanProperty(f.isHide()); + applied = new SimpleBooleanProperty(false); + } + + public BooleanProperty hideProperty(){ + return hideProp; + } + + public BooleanProperty appliedProperty(){ + return applied; + } + + @Override + public void setHide(boolean visible) { + super.setHide(visible); + hideProp.set(visible); + } + + @Override + public boolean isHide() { + return hideProp.get(); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/ChartColorManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/ChartColorManager.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot; + +import java.util.HashMap; +import java.util.Map; + +/** + * Color management class for chart and table cell. + * + * @author Yasumasa Suenaga + */ +public class ChartColorManager { + + private static final String[] chartColorArray; + + private static int colorIndex; + + private static final Map colorCache; + + static { + String colors[] = {"aquamarine", "bisque", "blueviolet", "brown", "blue", "chartreuse", "coral", + "cornflowerblue", "crimson", "darkcyan", "darkgoldenrod", "darkgreen", "darkkhaki", + "darkmagenta", "darkorange", "darksalmon", "darkseagreen", "deeppink", "dodgerblue", + "gold", "green", "orangered", "orchid", "plum", "red", "sandybrown", "slateblue", + "lime", "tomato", "turquoise", "violet", "orange"}; + + chartColorArray = colors; + colorIndex = 0; + colorCache = new HashMap<>(); + } + + /** + * Get next color from sequence. If color which relates className has been + * returned, this method returns same color. + * + * @param className Class name to get. + * @return Color string which relates className. + */ + public static String getNextColor(String className) { + String result = colorCache.computeIfAbsent(className, k -> chartColorArray[colorIndex++]); + + if (colorIndex == chartColorArray.length) { + colorIndex = 0; + } + + return result; + } + + /** + * Get class color from color cache. + * + * @param className Class name to get. + * @return Color string which relates className. transparent if className + * does not exist in cache. + */ + public static String getCurrentColor(String className) { + return colorCache.getOrDefault(className, "transparent"); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/SnapShotController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/SnapShotController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot; + +import java.io.File; +import java.net.URL; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javafx.application.Platform; +import javafx.beans.property.LongProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; +import javafx.collections.ObservableSet; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.chart.Axis; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.XYChart; +import javafx.scene.control.*; +import javafx.scene.layout.AnchorPane; +import javafx.scene.shape.Rectangle; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import jp.co.ntt.oss.heapstats.fx.MainWindowController; +import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; +import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; +import jp.co.ntt.oss.heapstats.container.snapshot.SummaryData; +import jp.co.ntt.oss.heapstats.api.plugin.PluginController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs.HistogramController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs.RefTreeController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs.SnapshotController; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs.SummaryController; +import jp.co.ntt.oss.heapstats.task.CSVDumpGC; +import jp.co.ntt.oss.heapstats.task.CSVDumpHeap; +import jp.co.ntt.oss.heapstats.task.ParseHeader; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; +import jp.co.ntt.oss.heapstats.fx.utils.TaskAdapter; + +/** + * FXML Controller of SnapShot builtin plugin. + * + * @author Yasumasa Suenaga + */ +public class SnapShotController extends PluginController implements Initializable { + + @FXML + private SummaryController summaryController; + + @FXML + private HistogramController histogramController; + + @FXML + private SnapshotController snapshotController; + + @FXML + private RefTreeController reftreeController; + + @FXML + private SplitPane rangePane; + + @FXML + private Label startTimeLabel; + + @FXML + private Label endTimeLabel; + + @FXML + private TextField snapshotList; + + @FXML + private RadioButton radioInstance; + + @FXML + private Button okBtn; + + @FXML + private TabPane snapshotMain; + + @FXML + private Tab histogramTab; + + @FXML + private Tab snapshotTab; + + @FXML + private Tab reftreeTab; + + private ObjectProperty> currentTarget; + + private ObjectProperty summaryData; + + private ObjectProperty> currentClassNameSet; + + private ObjectProperty> snapshotSelectionModel; + + private ObjectProperty>> topNList; + + private List snapShotHeaders; + + private ObjectProperty currentSnapShotHeader; + + private LongProperty currentObjectTag; + + private ObjectProperty rangeStart; + + private ObjectProperty rangeEnd; + + /** + * Update caption of label which represents time of selection. + * + * @param target Label compornent to draw. + * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 . + */ + private void updateRangeLabel(Label target, double newValue){ + if(!Optional.ofNullable(snapShotHeaders).map(List::isEmpty).orElse(true)){ + LocalDateTime start = snapShotHeaders.get(0).getSnapShotDate(); + LocalDateTime end = snapShotHeaders.get(snapShotHeaders.size() - 1).getSnapShotDate(); + long diff = start.until(end, ChronoUnit.MILLIS); + LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS); + + if(target == startTimeLabel){ + rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS)); + } + else{ + rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS)); + } + + target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter())); + } + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + super.initialize(url, rb); + + summaryData = new SimpleObjectProperty<>(); + summaryController.summaryDataProperty().bind(summaryData); + currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); + summaryController.currentTargetProperty().bind(currentTarget); + histogramController.currentTargetProperty().bind(currentTarget); + snapshotController.currentTargetProperty().bind(currentTarget); + currentClassNameSet = new SimpleObjectProperty<>(); + summaryController.currentClassNameSetProperty().bind(currentClassNameSet); + histogramController.currentClassNameSetProperty().bind(currentClassNameSet); + histogramController.instanceGraphProperty().bind(radioInstance.selectedProperty()); + snapshotController.instanceGraphProperty().bind(radioInstance.selectedProperty()); + snapshotSelectionModel = new SimpleObjectProperty<>(); + snapshotSelectionModel.bind(snapshotController.snapshotSelectionModelProperty()); + histogramController.snapshotSelectionModelProperty().bind(snapshotSelectionModel); + histogramController.setDrawRebootSuspectLine(this::drawRebootSuspectLine); + topNList = new SimpleObjectProperty<>(); + topNList.bind(histogramController.topNListProperty()); + snapshotController.topNListProperty().bind(topNList); + currentSnapShotHeader = new SimpleObjectProperty<>(); + currentSnapShotHeader.bind(snapshotController.snapshotSelectionModelProperty().get().selectedItemProperty()); + reftreeController.currentSnapShotHeaderProperty().bind(currentSnapShotHeader); + + currentObjectTag = new SimpleLongProperty(); + // TODO: + // Why can I NOT use binding? + // First binding is enabled. But second binding is disabled. + // So I use ChangeListener to avoid this issue. + //currentObjectTag.bind(histogramController.currentObjectTagProperty()); + //currentObjectTag.bind(snapshotController.currentObjectTagProperty()); + histogramController.currentObjectTagProperty().addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(m -> currentObjectTag.set((Long) m))); + snapshotController.currentObjectTagProperty().addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(m -> currentObjectTag.set((Long) m))); + reftreeController.currentObjectTagProperty().bind(currentObjectTag); + + snapshotMain.getSelectionModel().selectedItemProperty().addListener(this::onTabChanged); + + snapShotHeaders = null; + rangeStart = new SimpleObjectProperty<>(); + rangeEnd = new SimpleObjectProperty<>(); + + rangePane.getDividers().get(0).positionProperty().addListener((v, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue())); + rangePane.getDividers().get(1).positionProperty().addListener((v, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue())); + + setOnWindowResize((v, o, n) -> Platform.runLater(() -> Stream.of(summaryController.getHeapChart(), + summaryController.getInstanceChart(), + summaryController.getGcTimeChart(), + summaryController.getMetaspaceChart(), + histogramController.getTopNChart()) + .forEach(c -> Platform.runLater(() -> drawRebootSuspectLine(c))))); + + histogramController.setTaskExecutor(t -> { + bindTask(t); + (new Thread(t)).start(); + }); + } + + private void onTabChanged(ObservableValue observable, Tab oldValue, Tab newValue) { + if (newValue == reftreeTab) { + if (oldValue == histogramTab) { + currentObjectTag.set(histogramController.currentObjectTagProperty().get()); + } else if (oldValue == snapshotTab) { + currentObjectTag.set(snapshotController.currentObjectTagProperty().get()); + } + } + } + + /** + * onSucceeded event handler for ParseHeader. + * + * @param headers New SnapShotHeader list. + */ + private void onSnapShotParserSucceeded(List headers) { + snapShotHeaders = headers; + + rangePane.getDividers().get(0).setPosition(0.0d); + rangePane.getDividers().get(1).setPosition(1.0d); + + rangePane.setDisable(false); + okBtn.setDisable(false); + } + + /** + * Event handler of SnapShot file button. + * + * @param event ActionEvent of this event. + */ + @FXML + public void onSnapshotFileClick(ActionEvent event) { + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); + + dialog.setTitle(resource.getString("dialog.filechooser.title")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new ExtensionFilter("SnapShot file (*.dat)", "*.dat"), + new ExtensionFilter("All files", "*.*")); + + List snapshotFileList = dialog.showOpenMultipleDialog(MainWindowController.getInstance().getOwner()); + + if (snapshotFileList != null) { + clearAllItems(); + + HeapStatsUtils.setDefaultDirectory(snapshotFileList.get(0).getParent()); + List files = snapshotFileList.stream() + .map(File::getAbsolutePath) + .collect(Collectors.toList()); + snapshotList.setText(files.stream().collect(Collectors.joining("; "))); + + TaskAdapter task = new TaskAdapter<>(new ParseHeader(files, HeapStatsUtils.getReplaceClassName(), true)); + task.setOnSucceeded(evt -> onSnapShotParserSucceeded(task.getTask().getSnapShotList())); + super.bindTask(task); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + + } + + /** + * Event handler of OK button. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onOkClick(ActionEvent event) { + /* Get range */ + LocalDateTime start = rangeStart.getValue(); + LocalDateTime end = rangeEnd.getValue(); + currentTarget.set(FXCollections.observableArrayList(snapShotHeaders.stream() + .filter(d -> ((d.getSnapShotDate().compareTo(start) >= 0) && (d.getSnapShotDate().compareTo(end) <= 0))) + .collect(Collectors.toList()))); + currentClassNameSet.set(FXCollections.observableSet()); + summaryData.set(new SummaryData(currentTarget.get())); + + Task topNTask = histogramController.getDrawTopNDataTask(currentTarget.get(), true, null); + super.bindTask(topNTask); + Thread topNThread = new Thread(topNTask); + topNThread.start(); + + Task summarizeTask = summaryController.getCalculateGCSummaryTask(this::drawRebootSuspectLine); + super.bindTask(summarizeTask); + Thread summarizeThread = new Thread(summarizeTask); + summarizeThread.start(); + } + + /** + * Returns plugin name. This value is used to show in main window tab. + * + * @return Plugin name. + */ + @Override + public String getPluginName() { + return "SnapShot Data"; + } + + @Override + public EventHandler getOnPluginTabSelected() { + return null; + } + + @Override + public String getLicense() { + return PluginController.LICENSE_GPL_V2; + } + + @Override + public Map getLibraryLicense() { + Map licenseMap = new HashMap<>(); + licenseMap.put("JGraphX", PluginController.LICENSE_BSD); + + return licenseMap; + } + + /** + * Dump GC Statistics to CSV. + * + * @param isSelected If this value is true, this method dumps data which is + * selected time range, otherwise this method dumps all snapshot data. + */ + public void dumpGCStatisticsToCSV(boolean isSelected) { + FileChooser dialog = new FileChooser(); + dialog.setTitle("Select CSV files"); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new ExtensionFilter("CSV file (*.csv)", "*.csv"), + new ExtensionFilter("All files", "*.*")); + File csvFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); + + if (csvFile != null) { + TaskAdapter task = new TaskAdapter<>(new CSVDumpGC(csvFile, isSelected ? currentTarget.get() : snapShotHeaders)); + super.bindTask(task); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + + } + + /** + * Dump Java Class Histogram to CSV. + * + * @param isSelected If this value is true, this method dumps data which is + * selected in class filter, otherwise this method dumps all snapshot data. + */ + public void dumpClassHistogramToCSV(boolean isSelected) { + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); + + dialog.setTitle(resource.getString("dialog.csvchooser.title")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new ExtensionFilter("CSV file (*.csv)", "*.csv"), + new ExtensionFilter("All files", "*.*")); + File csvFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); + + if (csvFile != null) { + Predicate filter = histogramController.getFilter(); + TaskAdapter task = new TaskAdapter<>(new CSVDumpHeap(csvFile, isSelected ? currentTarget.get() : snapShotHeaders, isSelected ? filter : null, HeapStatsUtils.getReplaceClassName())); + super.bindTask(task); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + + } + + @Override + public Runnable getOnCloseRequest() { + return null; + } + + @Override + public void setData(Object data, boolean select) { + super.setData(data, select); + snapshotList.setText((String) data); + + TaskAdapter task = new TaskAdapter<>(new ParseHeader(Arrays.asList((String) data), HeapStatsUtils.getReplaceClassName(), true)); + task.setOnSucceeded(evt -> onSnapShotParserSucceeded(task.getTask().getSnapShotList())); + super.bindTask(task); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + + private void drawRebootSuspectLine(XYChart target) { + + if (target.getData().isEmpty() || target.getData().get(0).getData().isEmpty()) { + return; + } + + AnchorPane anchor = (AnchorPane) target.getParent().getChildrenUnmodifiable() + .stream() + .filter(n -> n instanceof AnchorPane) + .findAny() + .get(); + ObservableList anchorChildren = anchor.getChildren(); + anchorChildren.clear(); + + NumberAxis xAxis = (NumberAxis)target.getXAxis(); + Axis yAxis = target.getYAxis(); + Label chartTitle = (Label) target.getChildrenUnmodifiable().stream() + .filter(n -> n.getStyleClass().contains("chart-title")) + .findFirst() + .get(); + + double startX = xAxis.getLayoutX() + 4.0d; + double yPos = yAxis.getLayoutY() + chartTitle.getLayoutY() + chartTitle.getHeight(); + List rectList = summaryData.get().getRebootSuspectList() + .stream() + .map(d -> d.atZone(ZoneId.systemDefault()).toEpochSecond()) + .map(s -> new Rectangle(xAxis.getDisplayPosition(s) + startX, yPos, 4d, yAxis.getHeight())) + .peek(r -> ((Rectangle) r).setStyle("-fx-fill: yellow;")) + .collect(Collectors.toList()); + anchorChildren.addAll(rectList); + } + + /** + * Clear all items in SnapShot plugin. + */ + public void clearAllItems(){ + currentTarget.set(FXCollections.emptyObservableList()); + summaryData.set(null); + currentClassNameSet.set(FXCollections.emptyObservableSet()); + currentObjectTag.set(-1); + + summaryController.clearAllItems(); + histogramController.clearAllItems(); + snapshotController.clearAllItems(); +} + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/SnapShotHeaderConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/SnapShotHeaderConverter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot; + +import java.util.Optional; +import javafx.util.StringConverter; +import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * StringConverter for LocalDateTime of SnapShotHeader. + * This class is used at JavaFX controls. + * + * @author Yasumasa Suenaga + */ +public class SnapShotHeaderConverter extends StringConverter { + + @Override + public String toString(SnapShotHeader object) { + return Optional.ofNullable(object) + .map(o -> o.getSnapShotDate().format(HeapStatsUtils.getDateTimeFormatter())) + .orElse(null); + } + + @Override + public SnapShotHeader fromString(String string) { + throw new UnsupportedOperationException("SnapShotHeader DO NOT convert from String."); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/HistogramController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/HistogramController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2015-2019 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; + +import java.io.File; +import java.net.URL; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; +import javafx.collections.ObservableSet; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.StackedAreaChart; +import javafx.scene.chart.XYChart; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ListView; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.SelectionModel; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.CheckBoxTableCell; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.AnchorPane; +import javafx.stage.FileChooser; +import javax.xml.bind.JAXB; +import jp.co.ntt.oss.heapstats.container.snapshot.DiffData; +import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; +import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.BindingFilter; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.ChartColorManager; +import jp.co.ntt.oss.heapstats.task.DiffCalculator; +import jp.co.ntt.oss.heapstats.fx.utils.EpochTimeConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; +import jp.co.ntt.oss.heapstats.fx.utils.TaskAdapter; +import jp.co.ntt.oss.heapstats.xml.binding.Filter; +import jp.co.ntt.oss.heapstats.xml.binding.Filters; + +/** + * FXML Controller class for "Histogram" tab in SnapShot plugin. + */ +public class HistogramController implements Initializable { + + @FXML + private TableView excludeTable; + + @FXML + private TableColumn hideColumn; + + @FXML + private TableColumn excludeNameColumn; + + @FXML + private TextField searchText; + + @FXML + private ListView searchList; + + @FXML + private Button selectFilterApplyBtn; + + @FXML + private StackedAreaChart topNChart; + + @FXML + private AnchorPane topNChartAnchor; + + @FXML + private NumberAxis topNYAxis; + + @FXML + private TableView lastDiffTable; + + @FXML + private TableColumn colorColumn; + + @FXML + private TableColumn classNameColumn; + + @FXML + private TableColumn classLoaderColumn; + + @FXML + private TableColumn instanceColumn; + + @FXML + private TableColumn totalSizeColumn; + + private ObjectProperty> currentTarget; + + private ObjectProperty> currentClassNameSet; + + private BooleanProperty instanceGraph; + + private ObjectProperty> snapshotSelectionModel; + + private boolean searchFilterEnable; + + private boolean excludeFilterEnable; + + private ObjectProperty>> topNList; + + private LongProperty currentObjectTag; + + private Consumer> drawRebootSuspectLine; + + private Consumer> taskExecutor; + + private List hideRegexList; + + private EpochTimeConverter epochTimeConverter; + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + instanceGraph = new SimpleBooleanProperty(); + currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); + currentClassNameSet = new SimpleObjectProperty<>(FXCollections.emptyObservableSet()); + snapshotSelectionModel = new SimpleObjectProperty<>(); + topNList = new SimpleObjectProperty<>(); + currentObjectTag = new SimpleLongProperty(); + searchFilterEnable = false; + excludeFilterEnable = false; + + hideColumn.setCellValueFactory(new PropertyValueFactory<>("hide")); + hideColumn.setCellFactory(CheckBoxTableCell.forTableColumn(hideColumn)); + + excludeNameColumn.setCellFactory(p -> new TableCell(){ + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + BindingFilter filter = (BindingFilter)getTableRow().getItem(); + + if(!empty && (filter != null)){ + styleProperty().bind(Bindings.createStringBinding(() -> filter.appliedProperty().get() ? "-fx-background-color: skyblue;" : "-fx-background-color: white;", filter.appliedProperty())); + setText(filter.getName()); + } + + } + }); + + colorColumn.setCellFactory(p -> new TableCell() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + String style = Optional.ofNullable((DiffData) getTableRow().getItem()) + .filter(d -> d.isRanked()) + .map(d -> "-fx-background-color: " + ChartColorManager.getNextColor(d.getClassName())) + .orElse("-fx-background-color: transparent;"); + setStyle(style); + } + }); + + classNameColumn.setCellValueFactory(new PropertyValueFactory<>("className")); + classLoaderColumn.setCellValueFactory(new PropertyValueFactory<>("classLoaderName")); + instanceColumn.setCellValueFactory(new PropertyValueFactory<>("instances")); + instanceColumn.setSortType(TableColumn.SortType.DESCENDING); + totalSizeColumn.setCellValueFactory(new PropertyValueFactory<>("totalSize")); + totalSizeColumn.setSortType(TableColumn.SortType.DESCENDING); + + searchList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + + topNChart.lookup(".chart").setStyle("-fx-background-color: " + HeapStatsUtils.getChartBgColor() + ";"); + topNChart.getXAxis().setTickMarkVisible(HeapStatsUtils.getTickMarkerSwitch()); + + searchFilterEnable = false; + excludeFilterEnable = false; + + selectFilterApplyBtn.disableProperty().bind(searchList.selectionModelProperty().getValue().selectedItemProperty().isNull()); + currentObjectTag.bind(Bindings.createLongBinding(() -> Optional.ofNullable(lastDiffTable.getSelectionModel().getSelectedItem()) + .map(d -> d.getTag()) + .orElse(0xffffffffffffffffl), + lastDiffTable.getSelectionModel().selectedItemProperty())); + + epochTimeConverter = new EpochTimeConverter(); + } + + /** + * Build TopN Data for Chart with givien data. + * + * @param header SnapShot header which you want to build. + * @param seriesMap Chart series map which is contains class name as key, + * chart series as value. + * @param objData ObjectData which is you want to build. + */ + private void buildTopNChartData(SnapShotHeader header, Map> seriesMap, ObjectData objData) { + XYChart.Series series = seriesMap.get(objData.getName()); + + if (series == null) { + series = new XYChart.Series<>(); + series.setName(objData.getName()); + topNChart.getData().add(series); + seriesMap.put(objData.getName(), series); + } + + long time = header.getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); + long yValue = instanceGraph.get() ? objData.getCount() + : objData.getTotalSize() / 1024 / 1024; + XYChart.Data data = new XYChart.Data<>(time, yValue); + + series.getData().add(data); + String unit = instanceGraph.get() ? "instances" : "MB"; + String tip = String.format("%s: %s, %d " + unit, series.getName(), epochTimeConverter.toString(time), yValue); + Tooltip.install(data.getNode(), new Tooltip(tip)); + } + + private void setTopNChartColor(XYChart.Series series) { + String color = ChartColorManager.getNextColor(series.getName()); + + series.getNode().lookup(".chart-series-area-line").setStyle(String.format("-fx-stroke: %s;", color)); + series.getNode().lookup(".chart-series-area-fill").setStyle(String.format("-fx-fill: %s;", color)); + + series.getData().stream() + .map(d -> d.getNode().lookup(".chart-area-symbol")) + .forEach(n -> n.setStyle(String.format("-fx-background-color: %s, white;", color))); + } + + /** + * onSucceeded event handler for DiffTask. + * + * @param diff Target task. + * @param seriesMap Chart series map which is contains class name as key, + * chart series as value. + */ + private void onDiffTaskSucceeded(DiffCalculator diff, Map> seriesMap) { + /* Set chart range */ + long startEpoch = currentTarget.get().get(0).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); + long endEpoch = currentTarget.get().get(currentTarget.get().size() - 1).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); + NumberAxis xAxis = (NumberAxis)topNChart.getXAxis(); + xAxis.setTickUnit((endEpoch - startEpoch) / HeapStatsUtils.getXTickUnit()); + xAxis.setLowerBound(startEpoch); + xAxis.setUpperBound(endEpoch); + + topNList.set(FXCollections.observableMap(diff.getTopNList())); + + currentTarget.get().stream() + .forEachOrdered(h -> topNList.get().get(h.getSnapShotDate()).stream() + .forEachOrdered(o -> buildTopNChartData(h, seriesMap, o))); + + lastDiffTable.getItems().addAll(diff.getLastDiffList()); + lastDiffTable.getSortOrder().add(instanceGraph.get() ? instanceColumn : totalSizeColumn); + topNChart.getData().forEach(this::setTopNChartColor); + Platform.runLater(() -> drawRebootSuspectLine.accept(topNChart)); + + long maxVal = topNChart.getData().stream() + .flatMap(s -> s.dataProperty().get().stream()) + .collect(Collectors.groupingBy(d -> d.getXValue(), Collectors.summingLong(d -> d.getYValue()))) + .values() + .stream() + .mapToLong(Long::longValue) + .max() + .getAsLong(); + + topNYAxis.setUpperBound(maxVal * 1.05d); + topNYAxis.setTickUnit(maxVal / 20); + topNYAxis.setLabel(instanceGraph.get() ? "instances" : "MB"); + + snapshotSelectionModel.get().selectLast(); + + if(excludeFilterEnable){ + excludeTable.getItems().stream() + .forEach(f -> f.appliedProperty().set(f.isHide())); + } + else{ + excludeTable.getItems().stream() + .forEach(f -> f.appliedProperty().set(false)); + } + + } + + private void putIfAbsentToExcludeFilter(Filter filter){ + Optional exists = excludeTable.getItems().stream() + .filter(f -> f.getName().equals(filter.getName())) + .findAny(); + + if(exists.isPresent()){ + Alert dialog = new Alert(Alert.AlertType.ERROR, filter.getName() + " already exists!", ButtonType.OK); + dialog.showAndWait(); + } + else{ + excludeTable.getItems().add(new BindingFilter(filter)); + } + + } + + /** + * Event handler for adding exclude filter. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onAddClick(ActionEvent event) { + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); + + dialog.setTitle(resource.getString("dialog.filterchooser.title")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Filter file (*.xml)", "*.xml"), + new FileChooser.ExtensionFilter("All files", "*.*")); + + List excludeFilterList = dialog.showOpenMultipleDialog(((Node) event.getSource()).getScene().getWindow()); + if (excludeFilterList != null) { + excludeFilterList.stream() + .map(f -> (Filters) JAXB.unmarshal(f, Filters.class)) + .filter(f -> f != null) + .flatMap(f -> f.getFilter().stream()) + .forEach(this::putIfAbsentToExcludeFilter); + } + + } + + /** + * Drawing and Showing table with beging selected value. + * + * @return Class name filter. null if any filter is disabled. + */ + public Predicate getFilter() { + HashSet targetSet = new HashSet<>(searchList.getSelectionModel().getSelectedItems()); + Predicate searchFilter = o -> targetSet.contains(o.getName()); + Predicate hideFilter = o -> hideRegexList.stream().noneMatch(s -> o.getName().matches(s)); + + Predicate filter; + + if (searchFilterEnable && excludeFilterEnable) { + filter = searchFilter.and(hideFilter); + } else if (searchFilterEnable) { + filter = searchFilter; + } else if (excludeFilterEnable) { + filter = hideFilter; + } else { + filter = null; + } + + return filter; + } + + /** + * Event handler of apply button in exclude filter. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onHiddenFilterApply(ActionEvent event) { + excludeFilterEnable = true; + hideRegexList = excludeTable.getItems().stream() + .filter(f -> f.isHide()) + .flatMap(f -> f.getClasses().getName().stream()) + .map(s -> ".*" + s + ".*") + .collect(Collectors.toList()); + Predicate filter = getFilter(); + + taskExecutor.accept(getDrawTopNDataTask(currentTarget.get(), false, filter)); + } + + /** + * Event handler of changing search TextField. + * + * @param event KeyEvent of this event. + */ + @FXML + private void onSearchTextChanged(KeyEvent event) { + Stream searchStrings = currentClassNameSet.get().stream(); + + if(excludeFilterEnable){ + searchStrings = searchStrings.filter(n -> hideRegexList.stream().noneMatch(s -> n.matches(s))); + } + + searchList.getItems().clear(); + searchList.getItems().addAll(searchStrings.filter(n -> n.contains(searchText.getText())) + .collect(Collectors.toList())); + } + + /** + * Selection method for incremental search. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onSelectFilterApply(ActionEvent event) { + searchFilterEnable = true; + Predicate filter = getFilter(); + + taskExecutor.accept(getDrawTopNDataTask(currentTarget.get(), false, filter)); + } + + /** + * Event handler of clear button. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onSelectFilterClear(ActionEvent event) { + searchFilterEnable = false; + excludeFilterEnable = false; + + searchText.setText(""); + searchList.getItems().clear(); + + taskExecutor.accept(getDrawTopNDataTask(currentTarget.get(), true, null)); + } + + /** + * Get task for drawing Top N data to Chart and Table. + * + * @param target SnapShot to be drawed. + * @param includeOthers *Others* data should be included in this Top N data. + * @param predicate Filter function. + * @return Task for drawing Top N data. + */ + public Task getDrawTopNDataTask(List target, boolean includeOthers, Predicate predicate) { + topNChart.getData().clear(); + lastDiffTable.getItems().clear(); + Map> seriesMap = new HashMap<>(); + + TaskAdapter diff = new TaskAdapter<>(new DiffCalculator(target, HeapStatsUtils.getRankLevel(), + includeOthers, predicate, HeapStatsUtils.getReplaceClassName(), instanceGraph.get())); + diff.setOnSucceeded(evt -> onDiffTaskSucceeded(diff.getTask(), seriesMap)); + + return diff; + } + + /** + * Set Consumer for drawing reboot line. + * + * @param drawRebootSuspectLine Consumer for drawing reboot line. + */ + public void setDrawRebootSuspectLine(Consumer> drawRebootSuspectLine) { + this.drawRebootSuspectLine = drawRebootSuspectLine; + } + + /** + * Set Consumer for executing JavaFX task. This Consumer is used for tasks + * which should be shown ProgressIndicator. + * + * @param taskExecutor Consumer for executing JavaFX task. + */ + public void setTaskExecutor(Consumer> taskExecutor) { + this.taskExecutor = taskExecutor; + } + + /** + * Get property which contains chart category (instance count or object + * size). + * + * @return Property which contains chart category. + */ + public BooleanProperty instanceGraphProperty() { + return instanceGraph; + } + + /** + * Get property of list of SnapShotHeader. + * + * @return Property of list of SnapShotHeader. + */ + public ObjectProperty> currentTargetProperty() { + return currentTarget; + } + + /** + * Get property of class name set. + * + * @return Property of class name set. + */ + public ObjectProperty> currentClassNameSetProperty() { + return currentClassNameSet; + } + + /** + * Get property for current SnapShot selection. + * + * @return Property for current SnapShot selection. + */ + public ObjectProperty> snapshotSelectionModelProperty() { + return snapshotSelectionModel; + } + + /** + * Get TopN chart. + * + * @return TopN chart. + */ + public StackedAreaChart getTopNChart() { + return topNChart; + } + + /** + * Get property of Map of Top N list. + * + * @return Property of map of Top N list. + */ + public ObjectProperty>> topNListProperty() { + return topNList; + } + + /** + * Get property of current Object tag. + * + * @return Property of currentObjectTag. + */ + public LongProperty currentObjectTagProperty() { + return currentObjectTag; + } + + /** + * Get selected diff data. + * + * @return Selected diff data. + */ + public DiffData getSelectedData() { + return lastDiffTable.getSelectionModel().getSelectedItem(); + } + + /** + * Clear all items in Histogram tab. + */ + public void clearAllItems(){ + excludeTable.getItems().stream() + .forEach((f -> f.setHide(false))); + excludeFilterEnable = false; + + searchText.setText(""); + searchList.getItems().clear(); + searchFilterEnable = false; + + topNChart.getData().clear(); + topNChartAnchor.getChildren().clear(); + + lastDiffTable.getItems().clear(); +} + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/RefTreeController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/RefTreeController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2014-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; + +import javafx.scene.layout.VBox; +import jp.co.ntt.oss.heapstats.snapshot.ReferenceTracker; +import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxICell; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxGraphTransferable; +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.ResourceBundle; +import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.property.LongProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.embed.swing.SwingNode; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.RadioButton; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; +import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.SnapShotHeaderConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class of Reference Data tab. + * + * @author Yasumasa Suenaga + */ +public class RefTreeController implements Initializable, MouseListener { + + /** + * Reference to the region for displaying the object. + */ + private ReferenceGraph graph; + + /** + * Reference to the enclosing graph component. + */ + private mxGraphComponent graphComponent; + + @FXML + private VBox topVBox; + + @FXML + private Label snapshotLabel; + + @FXML + private RadioButton radioParent; + + @FXML + private RadioButton radioSize; + + @FXML + private CheckBox rankCheckBox; + + @FXML + private SwingNode graphNode; + + private ObjectProperty currentSnapShotHeader; + + private LongProperty currentObjectTag; + + private ResourceBundle resource; + + private void buildTab() { + if ((currentSnapShotHeader.get() != null) + && currentSnapShotHeader.get().hasReferenceData() + && (currentObjectTag.get() != -1)){ + SwingUtilities.invokeLater(() -> initializeSwingNode()); + } + else{ + /* + * Initialize SwingNode with dummy mxGraphComponent. + * SwingNode seems to hook focus ungrab event. + * If SwingNode is empty, IllegalArgumentException with "null source" is + * thrown when another stage (e.g. about dialog) is shown. + * To avoid this, dummy mxGraphComponent is set to SwingNode at first. + */ + SwingUtilities.invokeLater(() -> graphNode.setContent(new mxGraphComponent(new ReferenceGraph()))); + } + } + + /** + * Initialize method for SwingNode. This method is called by Swing Event + * Dispatcher Thread. + */ + private void initializeSwingNode() { + graph = new ReferenceGraph(); + graph.setCellsEditable(false); + + graph.getModel().beginUpdate(); + { + ObjectData data = currentSnapShotHeader.get().getSnapShot(HeapStatsUtils.getReplaceClassName()).get(currentObjectTag.get()); + if (data == null) { + throw new IllegalStateException(resource.getString("reftree.message.notselect")); + } + ReferenceCell cell = new ReferenceCell(data, true, false); + graph.addCell(cell); + } + graph.getModel().endUpdate(); + + graph.setEdgeStyle(); + + graphComponent = new mxGraphComponent(graph); + graphComponent.getGraphControl().addMouseListener(this); + graphComponent.setToolTips(true); + + graphNode.setContent(graphComponent); + + /* + * FIXME! + * SwingNode is not shown immediately. + * So I call repaint() method at last and request layout() call. + */ + graphComponent.repaint(); + Platform.runLater(() -> topVBox.layout()); + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + resource = rb; + ReferenceCell.initialize(); + currentSnapShotHeader = new SimpleObjectProperty<>(); + currentObjectTag = new SimpleLongProperty(); + currentObjectTag.addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(t -> buildTab())); + snapshotLabel.textProperty().bind(Bindings.createStringBinding(() -> (new SnapShotHeaderConverter()).toString(currentSnapShotHeader.get()), currentSnapShotHeader)); + + /* + * Initialize SwingNode with dummy mxGraphComponent. + * SwingNode seems to hook focus ungrab event. + * If SwingNode is empty, IllegalArgumentException with "null source" is + * thrown when another stage (e.g. about dialog) is shown. + * To avoid this, dummy mxGraphComponent is set to SwingNode at first. + */ + SwingUtilities.invokeLater(() -> { + mxGraphComponent dummyGraphComponent = new mxGraphComponent(new ReferenceGraph()); + graphNode.setContent(dummyGraphComponent); + }); + + } + + /** + * Add reference cells to graph. This method creates cell which represents + * child, and connects to it from parent. + * + * @param parentCell Parent cell + * @param child Child data. + */ + private void addReferenceCell(ReferenceCell parentCell, ObjectData objData) { + ReferenceCell cell = null; + mxCell tmp = (mxCell) parentCell.getParent(); + + for (int i = 0; i < tmp.getChildCount(); i++) { + mxICell target = tmp.getChildAt(i); + + if (target.isVertex() && (((ReferenceCell) target).getTag() == objData.getTag())) { + cell = (ReferenceCell) target; + } + + } + + ReferenceCell edge = new ReferenceCell(objData, false, true); + + if (cell == null) { + cell = new ReferenceCell(objData, false, false); + graph.addCell(cell); + } + + long size = objData.getTotalSize() / 1024; // KB + + edge.setValue((size == 0) ? objData.getCount() + "\n< 1" + : objData.getCount() + "\n" + objData.getTotalSize() / 1024); // KB + + graph.addEdge(edge, graph.getDefaultParent(), parentCell, cell, 1); + } + + /** + * Displayed on the graph to get a child or of a cell object you clicked, + * the information of the parent. + * + * @param parentCell Cell object you clicked + */ + private void drawMind(ReferenceCell parentCell) { + + if ((parentCell.getEdgeCount() > 1) + || (parentCell.isRoot() && (parentCell.getEdgeCount() > 0)) + || (parentCell.isEdge())) { + return; + } + + OptionalInt rankLevel = rankCheckBox.isSelected() ? OptionalInt.of(HeapStatsUtils.getRankLevel()) + : OptionalInt.empty(); + ReferenceTracker refTracker = new ReferenceTracker(currentSnapShotHeader.get().getSnapShot(HeapStatsUtils.getReplaceClassName()), rankLevel, Optional.empty()); + List objectList = radioParent.isSelected() ? refTracker.getParents(parentCell.getTag(), radioSize.isSelected()) + : refTracker.getChildren(parentCell.getTag(), radioSize.isSelected()); + + if (objectList.isEmpty()) { + return; + } + + graph.getModel().beginUpdate(); + { + objectList.forEach(o -> addReferenceCell(parentCell, o)); + + if (mxGraphTransferable.dataFlavor == null) { + try { + mxGraphTransferable.dataFlavor = new DataFlavor( + DataFlavor.javaJVMLocalObjectMimeType + "; class=com.mxgraph.swing.util.mxGraphTransferable", + null, new mxGraphTransferable(null, null).getClass().getClassLoader()); + } catch (ClassNotFoundException e) { + // do nothing + } + } + + mxHierarchicalLayout layout = new mxHierarchicalLayout(graph, SwingConstants.WEST); + layout.execute(graph.getDefaultParent()); + } + graph.getModel().endUpdate(); + } + + @Override + public void mouseClicked(MouseEvent e) { + Object cell = graphComponent.getCellAt(e.getX(), e.getY()); + + if ((e.getButton() == MouseEvent.BUTTON1) && (cell != null) && (cell instanceof ReferenceCell)) { + drawMind((ReferenceCell) cell); + } + + graphComponent.repaint(); + Platform.runLater(() -> topVBox.layout()); + } + + @Override + public void mousePressed(MouseEvent e) { + /* Nothing to do */ + } + + @Override + public void mouseReleased(MouseEvent e) { + /* Nothing to do */ + } + + @Override + public void mouseEntered(MouseEvent e) { + /* Nothing to do */ + } + + @Override + public void mouseExited(MouseEvent e) { + /* Nothing to do */ + } + + /** + * Event handler of OK button. + * + * @param event ActionEvent of this event. + */ + @FXML + private void onOkClick(ActionEvent event) { + + if ((currentSnapShotHeader.get() == null) + || (currentObjectTag.get() == -1)){ + return; + } else if (!currentSnapShotHeader.get().hasReferenceData()) { + // Version 1.0.x or "collect_reftree=false" do not have reftree data + Alert dialog = new Alert(AlertType.ERROR, resource.getString("reftree.nodata"), ButtonType.OK); + dialog.show(); + return; + } + + mxCell cell = (mxCell) graph.getDefaultParent(); + List removeCells = new ArrayList<>(); + + for (int i = 0; i < cell.getChildCount(); i++) { + mxCell removeCell = (mxCell) cell.getChildAt(i); + + if (removeCell.isEdge() || !((ReferenceCell) removeCell).isRoot()) { + removeCells.add(removeCell); + } else { + graph.removeSelectionCell(removeCell); + mxGeometry geometry = ((ReferenceCell) removeCell).getGeometry(); + geometry.setX(0); + geometry.setY(0); + ((ReferenceCell) removeCell).setGeometry(geometry); + } + + } + + graph.removeCells(removeCells.toArray()); + graph.refresh(); + graphComponent.refresh(); + graphComponent.getViewport().setViewPosition(new Point(0, 0)); + } + + /** + * Get property of current SnapShotHeader. + * + * @return Property of currentSnapShotHeader. + */ + public ObjectProperty currentSnapShotHeaderProperty() { + return currentSnapShotHeader; + } + + /** + * Get property of current Object tag. + * + * @return Property of currentObjectTag. + */ + public LongProperty currentObjectTagProperty() { + return currentObjectTag; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/ReferenceCell.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/ReferenceCell.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,155 @@ +/* + * ReferenceCell.java + * Created on 2012/11/18 + * + * Copyright (C) 2011-2019 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; + +import java.text.NumberFormat; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.util.mxConstants; +import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * extends {@link mxCell}
+ *
+ * Cells are the elements of the graph model. + */ +public class ReferenceCell extends mxCell { + + /** + * serialVersionUID. + */ + private static final long serialVersionUID = -4403355352725440764L; + + /** + * Defines the font size + */ + private static String FONT_SIZE; + /** + * Defines the zoom ratio by font size ratio. + */ + private static double ZOOM_RATIO; + + /** + * Defines the height of the cell. + */ + private static int CELL_HEIGHT; + + /** + * Define the width of the character in the cell. + */ + private static int CHAR_WIDTH; + + /** + * Information about the objects to display Map. + */ + private final ObjectData objectData; + + /** + * Holds information whether the cell as a starting point. + */ + private boolean rootCell; + + /** + * Constructs a new cell. + * + * @param data Information about the objects to display Map. + * @param root This cell is root or not. + * @param edge This cell is edge or not. + */ + public ReferenceCell(ObjectData data, boolean root, boolean edge) { + super(); + + objectData = data; + + if (edge) { + setEdge(true); + } else { + setValue(data.getName()); + setConnectable(false); + setVertex(true); + setStyle("fontSize="+FONT_SIZE); + + if (root) { + setStyle("shape=ellipse;fillColor=red;fontColor=black;fontSize="+FONT_SIZE); + } + + rootCell = root; + } + + setConnectable(false); + setGeometry(new mxGeometry(0, 0, CHAR_WIDTH * data.getName().length(), CELL_HEIGHT)); + } + + /** + * Initialize to define the size. + */ + public static void initialize() { + FONT_SIZE = String.valueOf(HeapStatsUtils.getFontSizeOfRefTree()); + ZOOM_RATIO = (double) HeapStatsUtils.getFontSizeOfRefTree() / mxConstants.DEFAULT_FONTSIZE; + CELL_HEIGHT = (int)(30 * ZOOM_RATIO); + CHAR_WIDTH = (int)(7 * ZOOM_RATIO); + } + + + /** + * Return the tag. + * + * @return Return the tag + */ + public final long getTag() { + return objectData.getTag(); + } + + /** + * It returns whether or not the cells to be the origin. + * + * @return It returns whether or not the cells to be the origin. + */ + public final boolean isRoot() { + return rootCell; + } + + @Override + public final String toString() { + NumberFormat format = NumberFormat.getInstance(); + StringBuilder buf = new StringBuilder(); + + if (isVertex()) { + buf.append(objectData.getLoaderName()); + buf.append(" (instances = "); + buf.append(format.format(objectData.getCount())); + buf.append("; heapsize = "); + buf.append(format.format(objectData.getTotalSize())); + buf.append(" bytes)"); + } else { + buf.append("instances = "); + buf.append(format.format(objectData.getCount())); + buf.append("; heapsize = "); + buf.append(format.format(objectData.getTotalSize())); + buf.append(" bytes"); + } + + return buf.toString(); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/ReferenceGraph.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/ReferenceGraph.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,67 @@ +/* + * ReferenceGraph.java + * Created on 2012/11/18 + * + * Copyright (C) 2011-2019 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; + +import java.util.HashMap; +import java.util.Map; + +import com.mxgraph.util.mxConstants; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxStylesheet; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * extends {@link mxGraph}
+ *
+ * Implements a graph object that allows to create diagrams from a graph model + * and stylesheet. + */ +public class ReferenceGraph extends mxGraph { + + @Override + public final String getToolTipForCell(Object cell) { + + if (cell instanceof ReferenceCell) { + return ((ReferenceCell) cell).toString(); + } + + return super.getToolTipForCell(cell); + } + + /** + * Sets the style for edges. + */ + public final void setEdgeStyle() { + Map edgeStyle = new HashMap<>(); + edgeStyle.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_CONNECTOR); + edgeStyle.put(mxConstants.STYLE_ENDARROW, mxConstants.ARROW_DIAMOND); + edgeStyle.put(mxConstants.STYLE_STROKECOLOR, "#000000"); + edgeStyle.put(mxConstants.STYLE_FONTCOLOR, "#000000"); + edgeStyle.put(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_RIGHT); + edgeStyle.put(mxConstants.STYLE_FONTSIZE, HeapStatsUtils.getFontSizeOfRefTree()); + + mxStylesheet style = new mxStylesheet(); + style.setDefaultEdgeStyle(edgeStyle); + setStylesheet(style); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/SnapshotController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/SnapshotController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; + +import java.net.URL; +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.stream.Collectors; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.chart.PieChart; +import javafx.scene.control.ComboBox; +import javafx.scene.control.SingleSelectionModel; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; +import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.ChartColorManager; +import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.SnapShotHeaderConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class for "SnapShot Data" tab in SnapShot plugin. + */ +public class SnapshotController implements Initializable { + + @FXML + private ComboBox snapShotTimeCombo; + + @FXML + private TableView> snapShotSummaryTable; + + @FXML + private TableColumn, String> snapShotSummaryKey; + + @FXML + private TableColumn, String> snapShotSummaryValue; + + @FXML + private PieChart usagePieChart; + + @FXML + private TableColumn objColorColumn; + + @FXML + private TableView objDataTable; + + @FXML + private TableColumn objClassNameColumn; + + @FXML + private TableColumn objClassLoaderColumn; + + @FXML + private TableColumn objInstancesColumn; + + @FXML + private TableColumn objSizeColumn; + + private ObjectProperty> currentTarget; + + private BooleanProperty instanceGraph; + + private ObjectProperty>> topNList; + + private LongProperty currentObjectTag; + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); + instanceGraph = new SimpleBooleanProperty(); + topNList = new SimpleObjectProperty<>(); + currentObjectTag = new SimpleLongProperty(); + + snapShotTimeCombo.itemsProperty().bind(currentTarget); + + snapShotTimeCombo.setConverter(new SnapShotHeaderConverter()); + snapShotSummaryKey.setCellValueFactory(new PropertyValueFactory<>("key")); + snapShotSummaryValue.setCellValueFactory(new PropertyValueFactory<>("value")); + + objColorColumn.setCellFactory(p -> new TableCell() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + String style = Optional.ofNullable((ObjectData) getTableRow().getItem()) + .map(o -> "-fx-background-color: " + ChartColorManager.getCurrentColor(o.getName()) + ";") + .orElse("-fx-background-color: transparent;"); + setStyle(style); + } + }); + + objClassNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + objClassLoaderColumn.setCellValueFactory(new PropertyValueFactory<>("loaderName")); + objInstancesColumn.setCellValueFactory(new PropertyValueFactory<>("count")); + objInstancesColumn.setSortType(TableColumn.SortType.DESCENDING); + objSizeColumn.setCellValueFactory(new PropertyValueFactory<>("totalSize")); + objSizeColumn.setSortType(TableColumn.SortType.DESCENDING); + + currentObjectTag.bind(Bindings.createLongBinding(() -> Optional.ofNullable(objDataTable.getSelectionModel().getSelectedItem()) + .map(o -> o.getTag()) + .orElse(0xffffffffffffffffl), + objDataTable.getSelectionModel().selectedItemProperty())); + } + + /** + * Event handler of SnapShot TIme. + * + * @param event ActionEvent of this event. + */ + @FXML + @SuppressWarnings("unchecked") + private void onSnapShotTimeSelected(ActionEvent event) { + SnapShotHeader header = snapShotTimeCombo.getSelectionModel().getSelectedItem(); + if (header == null) { + return; + } + + ObservableList> summaryList = snapShotSummaryTable.getItems(); + summaryList.clear(); + usagePieChart.getData().clear(); + objDataTable.getItems().clear(); + ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); + + summaryList.addAll( + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.date"), header.getSnapShotDate().format(HeapStatsUtils.getDateTimeFormatter())), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.hasreftree"), header.hasReferenceData() ? "Yes" : "N/A"), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.entries"), Long.toString(header.getNumEntries())), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.instances"), Long.toString(header.getNumInstances())), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.heap"), String.format("%.02f MB", (double) (header.getNewHeap() + header.getOldHeap()) / 1024.0d / 1024.0d)), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.metaspace"), String.format("%.02f MB", (double) (header.getMetaspaceUsage()) / 1024.0d / 1024.0d)), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.cause"), header.getCauseString()), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.gccause"), header.getGcCause()), + new AbstractMap.SimpleEntry<>(resource.getString("snapshot.gctime"), String.format("%d ms", header.getGcTime()))); + + usagePieChart.getData().addAll(topNList.get().get(header.getSnapShotDate()).stream() + .map(o -> new PieChart.Data(o.getName(), instanceGraph.get() ? o.getCount() : o.getTotalSize())) + .collect(Collectors.toList())); + usagePieChart.getData().stream() + .forEach(d -> d.getNode().setStyle("-fx-pie-color: " + ChartColorManager.getNextColor(d.getName()))); + + objDataTable.setItems(FXCollections.observableArrayList( + header.getSnapShot(HeapStatsUtils.getReplaceClassName()).values().stream().collect(Collectors.toList()))); + objDataTable.getSortOrder().add(instanceGraph.get() ? objInstancesColumn : objSizeColumn); + } + + /** + * Get property of list of SnapShotHeader. + * + * @return Property of list of SnapShotHeader. + */ + public ObjectProperty> currentTargetProperty() { + return currentTarget; + } + + /** + * Get property for current SnapShot selection. This property is same as + * SnapShot time ComboBox. + * + * @return Property for current SnapShot selection. + */ + public ObjectProperty> snapshotSelectionModelProperty() { + return snapShotTimeCombo.selectionModelProperty(); + } + + /** + * Get property which contains chart category (instance count or object + * size). + * + * @return Property which contains chart category. + */ + public BooleanProperty instanceGraphProperty() { + return instanceGraph; + } + + /** + * Get property of Map of Top N list. + * + * @return Property of map of Top N list. + */ + public ObjectProperty>> topNListProperty() { + return topNList; + } + + /** + * Get property of current Object tag. + * + * @return Property of currentObjectTag. + */ + public LongProperty currentObjectTagProperty() { + return currentObjectTag; + } + + /** + * Get Object Data table. + * + * @return Object Data table. + */ + public TableView getObjDataTable() { + return objDataTable; + } + + /** + * Clear all items in SnapShot tab. + */ + public void clearAllItems(){ + snapShotSummaryTable.getItems().clear(); + usagePieChart.getData().clear(); + objDataTable.getItems().clear(); +} + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/SummaryController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/SummaryController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2015-2019 Nippon Telegraph and Telephone Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; + +import java.net.URL; +import java.time.ZoneId; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.ObservableSet; +import javafx.concurrent.Task; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.chart.AreaChart; +import javafx.scene.chart.LineChart; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.StackedAreaChart; +import javafx.scene.chart.XYChart; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import javafx.scene.shape.Path; +import javafx.scene.shape.Rectangle; +import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; +import jp.co.ntt.oss.heapstats.container.snapshot.SummaryData; +import jp.co.ntt.oss.heapstats.fx.utils.EpochTimeConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * FXML Controller class for "Summary Data" tab in SnapShot plugin. + */ +public class SummaryController implements Initializable { + + @FXML + private TableView summaryTable; + + @FXML + private TableColumn keyColumn; + + @FXML + private TableColumn valueColumn; + + @FXML + private StackedAreaChart heapChart; + + private XYChart.Series youngUsage; + + private XYChart.Series oldUsage; + + private XYChart.Series free; + + @FXML + private LineChart instanceChart; + + private XYChart.Series instances; + + @FXML + private LineChart gcTimeChart; + + private XYChart.Series gcTime; + + @FXML + private AreaChart metaspaceChart; + + private XYChart.Series metaspaceUsage; + + private XYChart.Series metaspaceCapacity; + + private ObjectProperty summaryData; + + private ObjectProperty> currentTarget; + + private ObjectProperty> currentClassNameSet; + + private EpochTimeConverter epochTimeConverter; + + private Tooltip heapTooltip; + + private GridPane heapTooltipGrid; + + private Label youngLabel; + + private Label oldLabel; + + private Label freeLabel; + + private Tooltip metaspaceTooltip; + + private GridPane metaspaceTooltipGrid; + + private Label metaspaceUsageLabel; + + private Label metaspaceCapacityLabel; + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + summaryData = new SimpleObjectProperty<>(); + summaryData.addListener((v, o, n) -> setSummaryTable(n)); + currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); + currentClassNameSet = new SimpleObjectProperty<>(FXCollections.emptyObservableSet()); + + keyColumn.setCellValueFactory(new PropertyValueFactory<>("category")); + valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); + + String bgcolor = "-fx-background-color: " + HeapStatsUtils.getChartBgColor() + ";"; + Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) + .peek(c -> c.lookup(".chart").setStyle(bgcolor)) + .forEach(c -> c.getXAxis().setTickMarkVisible(HeapStatsUtils.getTickMarkerSwitch())); + + epochTimeConverter = new EpochTimeConverter(); + Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) + .map(c -> (NumberAxis)c.getXAxis()) + .forEach(a -> a.setTickLabelFormatter(epochTimeConverter)); + + initializeChartSeries(); + } + + private void setSummaryTable(SummaryData data) { + if (data == null) { + summaryTable.getItems().clear(); + } else { + ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); + String safepointTimeStr = data.hasSafepointTime() ? String.format("%d ms (%.02f %%)", data.getSafepointTime(), data.getSafepointPercentage()) + : "N/A"; + + summaryTable.setItems(FXCollections.observableArrayList(new SummaryData.SummaryDataEntry(resource.getString("summary.snapshot.count"), Integer.toString(data.getCount())), + new SummaryData.SummaryDataEntry(resource.getString("summary.gc.count"), String.format("%d (Full: %d, Young: %d)", data.getFullCount() + data.getYngCount(), data.getFullCount(), data.getYngCount())), + new SummaryData.SummaryDataEntry(resource.getString("summary.heap.usage"), String.format("%.1f MB", data.getLatestHeapUsage() / 1024.0d / 1024.0d)), + new SummaryData.SummaryDataEntry(resource.getString("summary.metaspace.usage"), String.format("%.1f MB", data.getLatestMetaspaceUsage() / 1024.0d / 1024.0d)), + new SummaryData.SummaryDataEntry(resource.getString("summary.gc.time"), String.format("%d ms", data.getMaxGCTime())), + new SummaryData.SummaryDataEntry(resource.getString("summary.gc.totaltime"), String.format("%d ms", data.getTotalGCTime())), + new SummaryData.SummaryDataEntry(resource.getString("summary.safepoint.time"), safepointTimeStr), + new SummaryData.SummaryDataEntry(resource.getString("summary.snapshot.size"), String.format("%.1f KB", data.getMaxSnapshotSize() / 1024.0d)), + new SummaryData.SummaryDataEntry(resource.getString("summary.snapshot.entrycount"), Long.toString(data.getMaxEntryCount())) + )); + } + } + + /** + * Initialize Series in Chart. This method uses to avoid RuntimeException + * which is related to: RT-37994: [FXML] ProxyBuilder does not support + * read-only collections https://javafx-jira.kenai.com/browse/RT-37994 + */ + @SuppressWarnings("unchecked") + private void initializeChartSeries() { + youngUsage = new XYChart.Series<>(); + youngUsage.setName("Young"); + oldUsage = new XYChart.Series<>(); + oldUsage.setName("Old"); + free = new XYChart.Series<>(); + free.setName("Free"); + + String cssName = "/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/"; + if (HeapStatsUtils.getHeapOrder()) { + heapChart.getData().addAll(youngUsage, oldUsage, free); + cssName += "heapsummary-bottom-young.css"; + } else { + heapChart.getData().addAll(oldUsage, youngUsage, free); + cssName += "heapsummary-bottom-old.css"; + } + heapChart.getStylesheets().add(SummaryController.class.getResource(cssName).toString()); + + instances = new XYChart.Series<>(); + instances.setName("Instances"); + instanceChart.getData().add(instances); + + gcTime = new XYChart.Series<>(); + gcTime.setName("GC Time"); + gcTimeChart.getData().add(gcTime); + + metaspaceCapacity = new XYChart.Series<>(); + metaspaceCapacity.setName("Capacity"); + metaspaceUsage = new XYChart.Series<>(); + metaspaceUsage.setName("Usage"); + metaspaceChart.getData().addAll(metaspaceCapacity, metaspaceUsage); + + /* Tooltip setup */ + /* Java heap */ + heapTooltipGrid = new GridPane(); + heapTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + youngLabel = new Label(); + oldLabel = new Label(); + freeLabel = new Label(); + Rectangle youngRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle oldRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle freeRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + youngRect.setStyle("-fx-fill: " + ((Path)heapChart.lookup(".series0")).getFill().toString().replace("0x", "#")); + oldRect.setStyle("-fx-fill: " + ((Path)heapChart.lookup(".series1")).getFill().toString().replace("0x", "#")); + freeRect.setStyle("-fx-fill: " + ((Path)heapChart.lookup(".series2")).getFill().toString().replace("0x", "#")); + }); + + heapTooltipGrid.add(youngRect, 0, 0); + heapTooltipGrid.add(new Label("Young"), 1, 0); + heapTooltipGrid.add(youngLabel, 2, 0); + heapTooltipGrid.add(oldRect, 0, 1); + heapTooltipGrid.add(new Label("Old"), 1, 1); + heapTooltipGrid.add(oldLabel, 2, 1); + heapTooltipGrid.add(freeRect, 0, 2); + heapTooltipGrid.add(new Label("Free"), 1, 2); + heapTooltipGrid.add(freeLabel, 2, 2); + heapTooltip = new Tooltip(); + heapTooltip.setGraphic(heapTooltipGrid); + heapTooltip.setContentDisplay(ContentDisplay.BOTTOM); + + /* Metaspace */ + metaspaceTooltipGrid = new GridPane(); + metaspaceTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); + metaspaceUsageLabel = new Label(); + metaspaceCapacityLabel = new Label(); + Rectangle metaUsageRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + Rectangle metaCapacityRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); + + Platform.runLater(() -> { + metaUsageRect.setStyle("-fx-fill: " + ((Path)metaspaceChart.lookup(".default-color0.chart-series-area-fill")).getFill().toString().replace("0x", "#")); + metaCapacityRect.setStyle("-fx-fill: " + ((Path)metaspaceChart.lookup(".default-color1.chart-series-area-fill")).getFill().toString().replace("0x", "#")); + }); + + metaspaceTooltipGrid.add(metaUsageRect, 0, 0); + metaspaceTooltipGrid.add(new Label("Usage"), 1, 0); + metaspaceTooltipGrid.add(metaspaceUsageLabel, 2, 0); + metaspaceTooltipGrid.add(metaCapacityRect, 0, 1); + metaspaceTooltipGrid.add(new Label("Capacity"), 1, 1); + metaspaceTooltipGrid.add(metaspaceCapacityLabel, 2, 1); + metaspaceTooltip = new Tooltip(); + metaspaceTooltip.setGraphic(metaspaceTooltipGrid); + metaspaceTooltip.setContentDisplay(ContentDisplay.BOTTOM); + } + + /** + * JavaFX task class for calculating GC summary. + */ + private class CalculateGCSummaryTask extends Task { + + private int processedIndex; + + private final Consumer> drawRebootSuspectLine; + + /* Java Heap Usage Chart */ + private final ObservableList> youngUsageBuf; + private final ObservableList> oldUsageBuf; + private final ObservableList> freeBuf; + + /* Instances */ + private final ObservableList> instanceBuf; + + /* GC time Chart */ + private final ObservableList> gcTimeBuf; + + /* Metaspace Chart */ + private final ObservableList> metaspaceUsageBuf; + private final ObservableList> metaspaceCapacityBuf; + + /** + * Constructor of CalculateGCSummaryTask. + * + * @param drawRebootSuspectLine Consumer for drawing reboot line. This + * consumer is called in Platform#runLater() at succeeded(). + */ + public CalculateGCSummaryTask(Consumer> drawRebootSuspectLine) { + this.drawRebootSuspectLine = drawRebootSuspectLine; + + youngUsageBuf = FXCollections.observableArrayList(); + oldUsageBuf = FXCollections.observableArrayList(); + freeBuf = FXCollections.observableArrayList(); + instanceBuf = FXCollections.observableArrayList(); + gcTimeBuf = FXCollections.observableArrayList(); + metaspaceUsageBuf = FXCollections.observableArrayList(); + metaspaceCapacityBuf = FXCollections.observableArrayList(); + } + + private void processSnapShotHeader(SnapShotHeader header) { + long time = header.getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); + + youngUsageBuf.add(new XYChart.Data<>(time, header.getNewHeap() / 1024 / 1024)); + oldUsageBuf.add(new XYChart.Data<>(time, header.getOldHeap() / 1024 / 1024)); + freeBuf.add(new XYChart.Data<>(time, (header.getTotalCapacity() - header.getNewHeap() - header.getOldHeap()) / 1024 / 1024)); + + instanceBuf.add(new XYChart.Data<>(time, header.getNumInstances())); + + gcTimeBuf.add(new XYChart.Data<>(time, header.getGcTime())); + + metaspaceUsageBuf.add(new XYChart.Data<>(time, header.getMetaspaceUsage() / 1024 / 1024)); + metaspaceCapacityBuf.add(new XYChart.Data<>(time, header.getMetaspaceCapacity() / 1024 / 1024)); + + currentClassNameSet.get().addAll(header.getSnapShot(HeapStatsUtils.getReplaceClassName()) + .values() + .stream() + .map(s -> s.getName()) + .collect(Collectors.toSet())); + + updateProgress(++processedIndex, currentTarget.get().size()); + } + + @Override + protected Void call() throws Exception { + updateMessage("Calcurating GC summary..."); + processedIndex = 0; + currentTarget.get().stream() + .forEachOrdered(d -> processSnapShotHeader(d)); + return null; + } + + private void setupJavaHeapChartTooltip(int idx){ + XYChart.Data youngNode = youngUsage.getData().get(idx); + XYChart.Data oldNode = oldUsage.getData().get(idx); + XYChart.Data freeNode = free.getData().get(idx); + long youngInMB = youngNode.getYValue(); + long oldInMB = oldNode.getYValue(); + long freeInMB = freeNode.getYValue(); + + EventHandler handler = e -> { + heapTooltip.setText(epochTimeConverter.toString(youngNode.getXValue())); + youngLabel.setText(youngInMB + " MB"); + oldLabel.setText(oldInMB + " MB"); + freeLabel.setText(freeInMB + " MB"); + }; + + Tooltip.install(youngNode.getNode(), heapTooltip); + youngNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(oldNode.getNode(), heapTooltip); + oldNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(freeNode.getNode(), heapTooltip); + freeNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + private void setupMetaspaceChartTooltip(int idx){ + XYChart.Data usageNode = metaspaceUsage.getData().get(idx); + XYChart.Data capacityNode = metaspaceCapacity.getData().get(idx); + long usage = usageNode.getYValue(); + long capacity = capacityNode.getYValue(); + + EventHandler handler = e -> { + metaspaceTooltip.setText(epochTimeConverter.toString(usageNode.getXValue())); + metaspaceUsageLabel.setText(usage + " MB"); + metaspaceCapacityLabel.setText(capacity + " MB"); + }; + + Tooltip.install(usageNode.getNode(), metaspaceTooltip); + usageNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + Tooltip.install(capacityNode.getNode(), metaspaceTooltip); + capacityNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); + } + + @Override + protected void succeeded() { + long startEpoch = currentTarget.get().get(0).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); + long endEpoch = currentTarget.get().get(currentTarget.get().size() - 1).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); + Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) + .peek(c -> ((NumberAxis)c.getXAxis()).setTickUnit((endEpoch - startEpoch) / HeapStatsUtils.getXTickUnit())) + .peek(c -> ((NumberAxis)c.getXAxis()).setLowerBound(startEpoch)) + .peek(c -> ((NumberAxis)c.getXAxis()).setUpperBound(endEpoch)) + .forEach(c -> Platform.runLater(() -> drawRebootSuspectLine.accept(c))); + + /* Replace new chart data */ + youngUsage.setData(youngUsageBuf); + oldUsage.setData(oldUsageBuf); + free.setData(freeBuf); + + instances.setData(instanceBuf); + + gcTime.setData(gcTimeBuf); + + metaspaceUsage.setData(metaspaceUsageBuf); + metaspaceCapacity.setData(metaspaceCapacityBuf); + + /* Set tooltip */ + /* Java Heap & Metaspace */ + IntStream.range(0, currentTarget.get().size()) + .peek(this::setupJavaHeapChartTooltip) + .forEach(this::setupMetaspaceChartTooltip); + + Tooltip tooltip = new Tooltip(); + + /* Insatances */ + instances.getData() + .stream() + .peek(d -> Tooltip.install(d.getNode(), tooltip)) + .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText((epochTimeConverter.toString(d.getXValue()) + ": " + d.getYValue())))); + + /* GC time */ + gcTime.getData() + .stream() + .peek(d -> Tooltip.install(d.getNode(), tooltip)) + .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText(String.format("%s: %d ms", epochTimeConverter.toString(d.getXValue()), d.getYValue())))); + } + + } + + /** + * Get property of SummaryData. + * + * @return Property of SummaryData. + */ + public ObjectProperty summaryDataProperty() { + return summaryData; + } + + /** + * Get property of list of SnapShotHeader. + * + * @return Property of list of SnapShotHeader. + */ + public ObjectProperty> currentTargetProperty() { + return currentTarget; + } + + /** + * Get property of class name set. + * + * @return Property of class name set. + */ + public ObjectProperty> currentClassNameSetProperty() { + return currentClassNameSet; + } + + /** + * Get new task for calculating GC summary. + * + * @param drawRebootSuspectLine Consumer for drawing reboot line. + * @return Task for calculating GC summary. + */ + public Task getCalculateGCSummaryTask(Consumer> drawRebootSuspectLine) { + return new CalculateGCSummaryTask(drawRebootSuspectLine); + } + + /** + * Get Java heap chart. + * + * @return Java heap chart. + */ + public StackedAreaChart getHeapChart() { + return heapChart; + } + + /** + * Get instance chart. + * + * @return Instance chart. + */ + public LineChart getInstanceChart() { + return instanceChart; + } + + /** + * Get GC time chart. + * + * @return GC time chart. + */ + public LineChart getGcTimeChart() { + return gcTimeChart; + } + + /** + * Get Metaspace chart. + * + * @return Metaspace chart. + */ + public AreaChart getMetaspaceChart() { + return metaspaceChart; + } + + /** + * Clear all items in SnapShot Summary tab. + */ + public void clearAllItems(){ + summaryTable.getItems().clear(); + + Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) + .peek(c -> c.getData().stream().forEach(s -> s.getData().clear())) // Clear chart series data. + .flatMap(c -> c.getParent().getChildrenUnmodifiable().stream()) // Get parent node of chart. + .filter(n -> n instanceof AnchorPane) + .forEach(p -> ((AnchorPane)p).getChildren().clear()); // Chart reboot suspect lines. +} + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/ThreadRecorderController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/ThreadRecorderController.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.threadrecorder; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.scene.control.cell.CheckBoxTableCell; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.FileChooser; +import jp.co.ntt.oss.heapstats.fx.MainWindowController; +import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; +import jp.co.ntt.oss.heapstats.api.plugin.PluginController; +import jp.co.ntt.oss.heapstats.task.ThreadRecordParseTask; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; +import jp.co.ntt.oss.heapstats.fx.utils.TaskAdapter; + +import java.io.File; +import java.net.URL; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Collectors; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + + +/** + * FXML Controller class + * + * @author Yasumasa Suenaga + */ +public class ThreadRecorderController extends PluginController implements Initializable { + + private static final int TIMELINE_PADDING = 8; + + @FXML + private TextField fileNameBox; + + @FXML + private Label startTimeLabel; + + @FXML + private Label endTimeLabel; + + @FXML + private TableColumn showColumn; + + @FXML + private TableColumn threadNameColumn; + + @FXML + private TableView timelineView; + + @FXML + private TableColumn> timelineColumn; + + @FXML + private SplitPane rangePane; + + @FXML + private Button okBtn; + + private List threadStatList; + + private Map idMap; + + private ObjectProperty rangeStart; + + private ObjectProperty rangeEnd; + + /** + * Update caption of label which represents time of selection. + * + * @param target Label compornent to draw. + * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 . + */ + private void updateRangeLabel(Label target, double newValue){ + if((threadStatList != null) && !threadStatList.isEmpty()){ + LocalDateTime start = threadStatList.get(0).getTime(); + LocalDateTime end = threadStatList.get(threadStatList.size() - 1).getTime(); + long diff = start.until(end, ChronoUnit.MILLIS); + LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS); + + if(target == startTimeLabel){ + rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS)); + } + else{ + rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS)); + } + + target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter())); + } + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + threadStatList = null; + rangeStart = new SimpleObjectProperty<>(); + rangeEnd = new SimpleObjectProperty<>(); + + rangePane.getDividers().get(0).positionProperty().addListener((b, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue())); + rangePane.getDividers().get(1).positionProperty().addListener((b, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue())); + + showColumn.setCellValueFactory(new PropertyValueFactory<>("show")); + showColumn.setCellFactory(CheckBoxTableCell.forTableColumn(showColumn)); + showColumn.setSortType(TableColumn.SortType.DESCENDING); + threadNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + timelineColumn.setCellValueFactory(new PropertyValueFactory<>("threadStats")); + timelineColumn.setCellFactory(param -> new TimelineCell(rangeStart, rangeEnd)); + timelineColumn.prefWidthProperty().bind(timelineView.widthProperty() + .subtract(showColumn.widthProperty()) + .subtract(threadNameColumn.widthProperty()) + .subtract(TIMELINE_PADDING)); + rangePane.getItems().forEach(n -> SplitPane.setResizableWithParent(n, false)); + } + + /** + * Event handler for open button. + * @param event Pushing open button event. + */ + @FXML + public void onOpenBtnClick(ActionEvent event){ + FileChooser dialog = new FileChooser(); + ResourceBundle resource = ResourceBundle.getBundle("threadrecorderResources", new Locale(HeapStatsUtils.getLanguage())); + + dialog.setTitle(resource.getString("dialog.filechooser.title")); + dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); + dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Thread Recorder file (*.htr)", "*.htr"), + new FileChooser.ExtensionFilter("All files", "*.*")); + File recorderFile = dialog.showOpenDialog(MainWindowController.getInstance().getOwner()); + + if(recorderFile != null){ + timelineView.getItems().clear(); + HeapStatsUtils.setDefaultDirectory(recorderFile.getParent()); + fileNameBox.setText(recorderFile.getAbsolutePath()); + + TaskAdapter task = new TaskAdapter<>(new ThreadRecordParseTask(recorderFile)); + super.bindTask(task); + task.setOnSucceeded(evt -> { + ThreadRecordParseTask parser = task.getTask(); + idMap = parser.getIdMap(); + threadStatList = parser.getThreadStatList(); + rangePane.getDividers().get(0).setPosition(0.0d); + rangePane.getDividers().get(1).setPosition(1.0d); + + rangePane.setDisable(false); + okBtn.setDisable(false); + + // FIX ME! Can we redraw more lightly? + timelineColumn.prefWidthProperty().addListener((b, o, n) -> onOkBtnClick(null)); + }); + + Thread parseThread = new Thread(task); + parseThread.start(); + } + + } + + /** + * Event handler for OK button. + * @param event Pushing ok button event. + */ + @FXML + private void onOkBtnClick(ActionEvent event){ + Map> statById = threadStatList.stream() + .filter(s -> s.getTime().isAfter(rangeStart.get()) && s.getTime().isBefore(rangeEnd.get())) + .collect(Collectors.groupingBy(ThreadStat::getId)); + ObservableList threadStats = FXCollections.observableArrayList(idMap.keySet().stream() + .sorted() + .map(k -> new ThreadStatViewModel(k, idMap.get(k), rangeStart.get(), rangeEnd.get(), statById.get(k))) + .collect(Collectors.toList())); + timelineView.setItems(threadStats); + timelineView.getSortOrder().add(showColumn); + } + + @Override + public String getPluginName() { + return "Thread Recorder"; + } + + @Override + public String getLicense() { + return PluginController.LICENSE_GPL_V2; + } + + @Override + public Map getLibraryLicense() { + return null; + } + + @Override + public EventHandler getOnPluginTabSelected() { + return null; + } + + @Override + public Runnable getOnCloseRequest() { + return null; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/ThreadStatViewModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/ThreadStatViewModel.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.threadrecorder; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.beans.property.SimpleBooleanProperty; +import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * Thread Status Data Model for presentation. + * + * @author Yasumasa Suenaga + */ +public class ThreadStatViewModel { + + private final BooleanProperty show; + + private long id; + + private String name; + + private LocalDateTime startTime; + + private LocalDateTime endTime; + + private final ReadOnlyObjectWrapper> threadStats; + + /** + * Constructor of ThreadStatViewModel + * + * @param id Thread ID + * @param name Thread Name + * @param startTime Start time + * @param endTime End time + * @param threadStats List of ThreadStat to draw. + */ + public ThreadStatViewModel(long id, String name, LocalDateTime startTime, LocalDateTime endTime, + List threadStats) { + this.id = id; + this.name = name; + this.startTime = startTime; + this.endTime = endTime; + this.threadStats = new ReadOnlyObjectWrapper<>(threadStats); + + if((threadStats == null) || threadStats.isEmpty()){ + this.show = new SimpleBooleanProperty(false); + } + else{ + this.show = new SimpleBooleanProperty(true); + } + + } + + /** + * Get show property. + * + * @return show property. + */ + public BooleanProperty showProperty(){ + return show; + } + + /** + * Get Thread ID. + * + * @return Thread ID. + */ + public long getId() { + return id; + } + + /** + * Set Thread ID. + * @param id New Thread ID. + */ + public void setId(long id) { + this.id = id; + } + + /** + * Get Thread name. + * @return Thread name. + */ + public String getName() { + return name; + } + + /** + * Set Thread name. + * @param name New Thread name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get Start time of Thread Recorder events. + * + * @return Start time. + */ + public LocalDateTime getStartTime() { + return startTime; + } + + /** + * Set Start time of Thread Recorder events. + * + * @param startTime New start time. + */ + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + /** + * Get End time of Thread Recorder events. + * + * @return End time. + */ + public LocalDateTime getEndTime() { + return endTime; + } + + /** + * Set End time of Thread Recorder events. + * + * @param endTime New end time. + */ + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + + /** + * Get list of ThreadStat. + * + * @return ThreadStat list. + */ + public List getThreadStats() { + return threadStats.get(); + } + + /** + * Get threadStats property. + * @return threadStats property. + */ + public ReadOnlyObjectProperty> threadStatsProperty() { + return threadStats.getReadOnlyProperty(); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/TimelineCell.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/TimelineCell.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015-2019 Takashi Aoe + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.threadrecorder; + +import javafx.geometry.Pos; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.TableCell; +import javafx.scene.layout.HBox; +import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; + +import java.time.LocalDateTime; +import java.util.List; +import javafx.beans.property.ObjectProperty; + +/** + * Table cell for thread timeline. + */ +public class TimelineCell extends TableCell> { + + private final ObjectProperty rangeStart; + + private final ObjectProperty rangeEnd; + + /** + * Constructor of TielineCell. + * + * @param rangeStart Start time. + * @param rangeEnd End time. + */ + public TimelineCell(ObjectProperty rangeStart, ObjectProperty rangeEnd) { + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + setAlignment(Pos.CENTER_LEFT); + + this.rangeStart = rangeStart; + this.rangeEnd = rangeEnd; + } + + @Override + protected void updateItem(List item, boolean empty) { + super.updateItem(item, empty); + ThreadStatViewModel model = (ThreadStatViewModel)getTableRow().getItem(); + + if (empty || (item == null) || item.isEmpty() || (model == null)) { + updateToEmptyCell(); + } else { + drawTimeline(model); + } + + } + + private void drawTimeline(ThreadStatViewModel viewModel) { + TimelineGenerator generator = new TimelineGenerator(viewModel, getTableColumn().prefWidthProperty()); + HBox container = generator.createTimeline(rangeStart.get(), rangeEnd.get()); + + container.visibleProperty().bind(viewModel.showProperty()); + setGraphic(container); + } + + private void updateToEmptyCell() { + setText(null); + setGraphic(null); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/TimelineGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/threadrecorder/TimelineGenerator.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2015-2019 Takashi Aoe + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.plugin.builtin.threadrecorder; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import java.util.ArrayList; +import java.util.List; +import javafx.beans.property.DoubleProperty; +import javafx.geometry.Pos; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.HBox; +import javafx.scene.shape.Rectangle; +import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; + +/** + * Generate Thread Recorder timeline. + * + * @author Yasumasa Suenaga + */ +public class TimelineGenerator { + + private static final double RECT_HEIGHT = 16; + + private static final String CSS_CLASS_PREFIX = "rect-"; + + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"); + + public static enum ThreadEvent{ + Unused, + Run, + MonitorWait, + MonitorContended, + ThreadSleep, + Park, + FileWrite, + FileRead, + SocketWrite, + SocketRead + } + + private final ThreadStatViewModel viewModel; + + private final DoubleProperty prefWidth; + + private long range; + + private double scale; + + private TemporalUnit unit; + + /** + * Constructor of TimelineGenerator. + * + * @param viewModel ViewModel to draw. + * @param prefWidth prefWidth to bind. + */ + public TimelineGenerator(ThreadStatViewModel viewModel, DoubleProperty prefWidth) { + this.viewModel = viewModel; + this.prefWidth = prefWidth; + } + + private void decideDrawScale(LocalDateTime start, LocalDateTime end){ + double width = prefWidth.get(); + + // Try to milli sec + unit = ChronoUnit.MILLIS; + range = start.until(end, unit); + scale = width / (double)range; + + // Try to micro sec + if(scale > 1.0d){ + unit = ChronoUnit.MICROS; + range = start.until(end, unit); + scale = width / (double)range; + + // Try to nano sec + if(scale > 1.0d){ + unit = ChronoUnit.NANOS; + range = start.until(end, unit); + scale = width / (double)range; + } + + } + + } + + /** + * Create timeline cells. + * + * @return HBox which includes Thread Recorder events. + */ + public HBox createTimeline(){ + List threadStatList = viewModel.threadStatsProperty().get(); + + if(threadStatList.isEmpty()){ + return new HBox(); + } + + LocalDateTime start = threadStatList.get(0).getTime(); + LocalDateTime end = threadStatList.get(threadStatList.size() - 1).getTime(); + + return createTimeline(start, end); + } + + /** + * Create timeline cells. + * + * @param start Start time. + * @param end End time. + * @return HBox which includes Thread Recorder events. + */ + public HBox createTimeline(LocalDateTime start, LocalDateTime end){ + HBox container = new HBox(); + container.setAlignment(Pos.CENTER_LEFT); + container.prefWidthProperty().bind(prefWidth); + + decideDrawScale(start, end); + drawTimeline(start, container); + + return container; + } + + private ThreadEvent convertToThreadEvent(ThreadStat.ThreadEvent event){ + switch (event) { + case ThreadStart: + case MonitorWaited: + case MonitorContendedEntered: + case ThreadSleepEnd: + case Unpark: + case FileWriteEnd: + case FileReadEnd: + case SocketWriteEnd: + case SocketReadEnd: + return ThreadEvent.Run; + + case ThreadEnd: + return ThreadEvent.Unused; + + case MonitorWait: + return ThreadEvent.MonitorWait; + + case MonitorContendedEnter: + return ThreadEvent.MonitorContended; + + case ThreadSleepStart: + return ThreadEvent.ThreadSleep; + + case Park: + return ThreadEvent.Park; + + case FileWriteStart: + return ThreadEvent.FileWrite; + + case FileReadStart: + return ThreadEvent.FileRead; + + case SocketWriteStart: + return ThreadEvent.SocketWrite; + + case SocketReadStart: + return ThreadEvent.SocketRead; + + default: + return ThreadEvent.Unused; + } + } + + private void drawTimeline(LocalDateTime start, HBox container) { + List rects = new ArrayList<>(); + int startIndex; + LocalDateTime prevTime; + ThreadEvent prevEvent; + long prevAdditionalData; + List threadStatList = viewModel.threadStatsProperty().get(); + + if(threadStatList.isEmpty()){ + return; + } + else{ + ThreadStat threadStat = threadStatList.get(0); + prevTime = threadStat.getTime(); + + if(start.equals(prevTime)){ + startIndex = 1; + prevTime = threadStat.getTime(); + prevEvent = convertToThreadEvent(threadStat.getEvent()); + prevAdditionalData = threadStat.getAdditionalData(); + } + else{ + startIndex = 0; + prevTime = start; + prevEvent = ThreadEvent.Unused; + prevAdditionalData = 0; + } + + } + + Rectangle fusionRect = new Rectangle(0, RECT_HEIGHT); + fusionRect.getStyleClass().add(CSS_CLASS_PREFIX + "fusion"); + + for(int Cnt = startIndex; Cnt < threadStatList.size(); Cnt++){ + ThreadStat threadStat = threadStatList.get(Cnt); + long additionalData; + + if((prevEvent == ThreadEvent.FileRead) || (prevEvent == ThreadEvent.FileWrite) || + (prevEvent == ThreadEvent.SocketRead) || (prevEvent == ThreadEvent.SocketWrite)){ + additionalData = threadStat.getAdditionalData(); + } + else{ + additionalData = prevAdditionalData; + } + + Rectangle rect = createThreadRect(prevTime, threadStat.getTime(), prevEvent, additionalData); + + if(rect.getWidth() < 1.0d){ + fusionRect.setWidth(fusionRect.getWidth() + rect.getWidth()); + } + else{ + + if(fusionRect.getWidth() > 0.0d){ + rects.add(fusionRect); + fusionRect = new Rectangle(0, RECT_HEIGHT); + fusionRect.getStyleClass().add(CSS_CLASS_PREFIX + "fusion"); + Tooltip.install(fusionRect, new Tooltip("Very short time event.")); + } + + rects.add(rect); + } + + prevTime = threadStat.getTime(); + prevAdditionalData = threadStat.getAdditionalData(); + prevEvent = convertToThreadEvent(threadStat.getEvent()); + } + + if(fusionRect.getWidth() > 0.0d){ + rects.add(fusionRect); + Tooltip.install(fusionRect, new Tooltip("Very short time event.")); + } + + container.getChildren().addAll(rects); + } + + private Rectangle createThreadRect(LocalDateTime startTime, LocalDateTime endTime, ThreadEvent event, long additionalData) { + long timeDiff = startTime.until(endTime, unit); + double width = scale * timeDiff; + Rectangle rectangle = new Rectangle(width, RECT_HEIGHT); + String styleClass = CSS_CLASS_PREFIX + event.name().toLowerCase(); + rectangle.getStyleClass().add(styleClass); + + if(width > 1.0d){ + String caption = startTime.format(formatter) + " - " + endTime.format(formatter) + ": " + event.toString(); + switch(event){ + case MonitorWait: + case ThreadSleep: + case Park: + if(additionalData > 0){ + caption += " (" + Long.toString(additionalData) + " ms)"; + } + break; + + case FileWrite: + case FileRead: + case SocketWrite: + case SocketRead: + caption += " (" + Long.toString(additionalData) + " bytes)"; + break; + } + + if((event != ThreadEvent.Unused)){ + Tooltip.install(rectangle, new Tooltip(caption)); + } + + } + + return rectangle; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/utils/EpochTimeConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/utils/EpochTimeConverter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.utils; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import javafx.util.StringConverter; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * String converter for epoch time. + * This class supports SECOND time unit ONLY. + * + * @author Yasumasa Suenaga + */ +public class EpochTimeConverter extends StringConverter{ + + @Override + public String toString(Number object) { + return LocalDateTime.ofInstant(Instant.ofEpochSecond(object.longValue()), ZoneId.systemDefault()).format(HeapStatsUtils.getDateTimeFormatter()); + } + + @Override + public Number fromString(String string) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/utils/TaskAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/utils/TaskAdapter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.utils; + +import javafx.application.Platform; +import javafx.concurrent.Task; +import jp.co.ntt.oss.heapstats.task.ProgressRunnable; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * Adapter class for Task in Java FX. + * This class adapt ProgressRunnable to Java FX Void Task. + * + * @author Yasumasa Suenaga + * @param Implementation of ProgressRunnable + */ +public class TaskAdapter extends Task{ + + private final T task; + + /** + * Constructor of TaskAdapter. + * + * @param task Instance of ProgressRunnable + */ + public TaskAdapter(T task){ + this.task = task; + this.task.setUpdateProgress(p -> updateProgress(p, task.getTotal())); + } + + /** + * Get ProgressRunnable instance which is related to this task. + * @return Instance of ProgressRunnable + */ + public T getTask() { + return task; + } + + @Override + protected Void call() throws Exception { + + try{ + task.run(); + } + catch(Exception e){ + Platform.runLater(() -> HeapStatsUtils.showExceptionDialog(e)); + } + + return null; + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/utils/ThreadStatConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/utils/ThreadStatConverter.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015-2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package jp.co.ntt.oss.heapstats.fx.utils; + +import javafx.util.StringConverter; +import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; +import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; + +/** + * StringConverter for LocalDateTime of SnapShotHeader. + * This class is used at JavaFX controls. + * + * @author Yasumasa Suenaga + */ +public class ThreadStatConverter extends StringConverter{ + + @Override + public String toString(ThreadStat object) { + return object.getTime().format(HeapStatsUtils.getDateTimeFormatter()); + } + + @Override + public ThreadStat fromString(String string) { + throw new UnsupportedOperationException("ThreadStat DO NOT convert from String."); + } + +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveConfig.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveConfig.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2014-2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Properties; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter.ErrorReportServer; -import jp.co.ntt.oss.heapstats.utils.HeapStatsConfigException; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * Configuration of JVMLive. - * - * @author Yasumasa Suenaga - */ -public class JVMLiveConfig { - - public static final String JDP_WAIT_DURATION_KEY = "jdp.waitDuration"; - - public static final String THREADPOOL_SHOTDOWN_AWAIT_TIME_KEY = "threadpool.shotdownAwaitTime"; - - public static final String ERRORREPORT_SERVER_PORT_KEY = "errorreport.server.port"; - - private static final Properties prop = new Properties(); - - /** - * Check and set value to JVMListConfig. - * - * @param key configuration key. - * @param defaultValue default value of configuration key. - * @throws HeapStatsConfigException when invalid HeapStats config is set. - */ - private static void setupNumericProperty(String key, int defaultValue) throws HeapStatsConfigException{ - String valStr = prop.getProperty(key); - - if(valStr == null){ - prop.setProperty(key, Integer.toString(defaultValue)); - } - else{ - try{ - Integer.decode(valStr); - } - catch(NumberFormatException e){ - throw new HeapStatsConfigException(String.format("Invalid option: %s=%s", key, valStr), e); - } - } - - } - - /** - * Load configuration from <HeapStats home directory>/jvmlive.properties . - * - * @throws IOException when properties file cannot be read. - * @throws HeapStatsConfigException when invalid HeapStats config is set. - */ - public static void load() throws IOException, HeapStatsConfigException{ - Path properties = Paths.get(HeapStatsUtils.getHeapStatsHomeDirectory().toString(), "jvmlive.properties"); - - try(InputStream in = Files.newInputStream(properties, StandardOpenOption.READ)){ - prop.load(in); - } - catch(NoSuchFileException e){ - // use default values. - } - - setupNumericProperty(JDP_WAIT_DURATION_KEY, 3); - setupNumericProperty(THREADPOOL_SHOTDOWN_AWAIT_TIME_KEY, 5); - setupNumericProperty(ERRORREPORT_SERVER_PORT_KEY, ErrorReportServer.DEFAULT_ERROR_REPORT_SERVER_PORT); - } - - /** - * Get a Wait duration for JDP packet. - * This value uses as duration for JdpValidatorService. - * - * @return Wait duration for JDP packet. - */ - public static int getJdpWaitDuration(){ - return Integer.parseInt(prop.getProperty(JDP_WAIT_DURATION_KEY)); - } - - /** - * Get a wait duration time for JDP packet. - * @return Await time for threadpool shutdown. - */ - public static int getThreadpoolShutdownAwaitTime(){ - return Integer.parseInt(prop.getProperty(THREADPOOL_SHOTDOWN_AWAIT_TIME_KEY)); - } - - /** - * Get a prot of error report server. - * @return Port for error report receiver. - */ - public static int getErrorReportServerPort(){ - return Integer.parseInt(prop.getProperty(ERRORREPORT_SERVER_PORT_KEY)); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.UnknownHostException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.rmi.ConnectException; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javafx.beans.binding.Bindings; -import javafx.concurrent.ScheduledService; -import javafx.event.ActionEvent; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Labeled; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.control.MenuItem; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextArea; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.image.Image; -import javafx.scene.input.MouseButton; -import javafx.scene.input.MouseEvent; -import javafx.stage.FileChooser; -import javafx.stage.Modality; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.util.Duration; -import jp.co.ntt.oss.heapstats.MainWindowController; -import jp.co.ntt.oss.heapstats.jmx.JMXHelper; -import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; -import jp.co.ntt.oss.heapstats.plugin.PluginController; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter.ErrorReportDecoder; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter.ErrorReportServer; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpDecoder; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpReceiver; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpTableKeyValue; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpValidatorService; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.mbean.HeapStatsMBeanController; -import jp.co.ntt.oss.heapstats.utils.HeapStatsConfigException; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class for JVMLive - * - * @author Yasumasa Suenaga - */ -public class JVMLiveController extends PluginController implements Initializable { - - @FXML - private ListView jvmList; - - @FXML - private TableView detailTable; - - @FXML - private TableColumn jdpKey; - - @FXML - private TableColumn jdpValue; - - @FXML - private ListView crashList; - - @FXML - private MenuItem detailsMenu; - - @FXML - private MenuItem saveMenu; - - private JdpReceiver jdpReceiver; - - private Thread receiverThread; - - private ScheduledService jdpValidator; - - private ExecutorService taskThreadPool; - - private ExecutorService jmxThreadPool; - - private ErrorReportServer errorReportServer; - - private Thread errorReportThread; - - private Optional jconsolePath; - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - - try { - JVMLiveConfig.load(); - } catch (IOException | HeapStatsConfigException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - - jdpKey.setCellValueFactory(new PropertyValueFactory<>("key")); - jdpValue.setCellValueFactory(new PropertyValueFactory<>("value")); - jdpValue.setCellFactory(c -> new TableCell(){ - @Override - protected void updateItem(Object item, boolean empty) { - super.updateItem(item, empty); - Optional.ofNullable(item).ifPresent(i -> { - try{ - if(i instanceof Labeled){ - setGraphic((Labeled)i); - } - else if(i instanceof JMXHelper){ - setGraphic(createHyperlink((JMXHelper)i)); - } - else{ - setText((String)i); - } - } - catch(Throwable t){ - setText(""); - } - }); - } - } - ); - - jvmList.getSelectionModel().selectedItemProperty() - .addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(d -> showJdpDecoderDetail((JdpDecoder)d))); - - jvmList.setCellFactory(l -> new ListCell(){ - @Override - protected void updateItem(JdpDecoder jdp, boolean b) { - super.updateItem(jdp, b); - Optional.ofNullable(jdp) - .ifPresent(d -> { - setText(d.toString()); - styleProperty().bind(Bindings.createStringBinding(() -> d.invalidateProperty().get() ? "-fx-text-fill: orange;" : "-fx-text-fill: black;", d.invalidateProperty())); - }); - } - } - ); - - Path jdkBase = Paths.get(Paths.get(System.getProperties().getProperty("java.home")).getParent().toString(), "bin"); - File f = new File(jdkBase.toString(), "jconsole"); - if(f.canExecute()){ - jconsolePath = Optional.of(f.getAbsolutePath()); - } - else{ - f = new File(jdkBase.toString(), "jconsole.exe"); - if(f.canExecute()){ - jconsolePath = Optional.of(f.getAbsolutePath()); - } - else{ - jconsolePath = Optional.empty(); - } - } - - - taskThreadPool = Executors.newCachedThreadPool(); - jmxThreadPool = Executors.newCachedThreadPool(); - - try { - jdpReceiver = new JdpReceiver(jvmList, taskThreadPool, jconsolePath, jmxThreadPool); - receiverThread = new Thread(jdpReceiver, "JDP receiver thread"); - receiverThread.start(); - - jdpValidator = new JdpValidatorService(jvmList); - jdpValidator.setPeriod(Duration.seconds(2)); - jdpValidator.start(); - } catch (UnknownHostException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - - errorReportServer = new ErrorReportServer(JVMLiveConfig.getErrorReportServerPort(), crashList.getItems(), taskThreadPool); - errorReportThread = new Thread(errorReportServer, "ErrorReport receiver thread"); - errorReportThread.start(); - - detailsMenu.disableProperty().bind(crashList.selectionModelProperty().getValue().selectedItemProperty().isNull()); - saveMenu.disableProperty().bind(crashList.selectionModelProperty().getValue().selectedItemProperty().isNull()); - } - - private void onHeapStatsLinkClicked(JMXHelper jmxHelper){ - ResourceBundle pluginResource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - FXMLLoader loader = new FXMLLoader(JVMLiveController.class.getResource("/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/heapstatsMBean.fxml"), pluginResource); - HeapStatsMBeanController mbeanController; - Scene mbeanDialogScene; - - try { - loader.load(); - mbeanController = (HeapStatsMBeanController)loader.getController(); - mbeanDialogScene = new Scene(loader.getRoot()); - } - catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - return; - } - - mbeanController.setJmxHelper(jmxHelper); - mbeanController.loadAllConfigs(); - Stage dialog = new Stage(StageStyle.UTILITY); - - try(InputStream icon = MainWindowController.class.getResourceAsStream("heapstats-icon.png")){ - dialog.getIcons().add(new Image(icon)); - } - catch(IOException e){ - HeapStatsUtils.showExceptionDialog(e); - } - - dialog.setScene(mbeanDialogScene); - dialog.initModality(Modality.APPLICATION_MODAL); - dialog.setResizable(false); - dialog.setTitle("HeapStats configuration @ " + jmxHelper.getUrl().toString()); - dialog.showAndWait(); - } - - private Hyperlink createHyperlink(JMXHelper jmxHelper){ - Hyperlink heapstatsLink = new Hyperlink(jmxHelper.getMbean().getHeapStatsVersion()); - heapstatsLink.setOnAction(e -> onHeapStatsLinkClicked(jmxHelper)); - - return heapstatsLink; - } - - private void showHsErrDialog(ErrorReportDecoder data){ - String host = data.toString(); - String hsErr; - - try(BufferedReader reader = Files.newBufferedReader(data.getHsErrFile().toPath())){ - hsErr = reader.lines().collect(Collectors.joining("\n")); - } catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - return; - } - - Alert dialog = new Alert(AlertType.WARNING); - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - - dialog.setTitle(resource.getString("dialog.crashreport.title")); - dialog.setHeaderText(resource.getString("dialog.crashreport.header") + host); - TextArea hsErrArea = new TextArea(hsErr); - hsErrArea.setEditable(false); - dialog.getDialogPane().setExpandableContent(hsErrArea); - dialog.showAndWait(); - } - - @FXML - private void onCrashHistoryClicked(MouseEvent event){ - if(event.getButton().equals(MouseButton.PRIMARY) && (event.getClickCount() == 2)){ - showHsErrDialog(crashList.getSelectionModel().getSelectedItem()); - } - } - - @FXML - private void onDetailsMenuClicked(ActionEvent event){ - showHsErrDialog(crashList.getSelectionModel().getSelectedItem()); - } - - @FXML - private void onSaveMenuClicked(ActionEvent event){ - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - - dialog.setTitle(resource.getString("dialog.crashreport.save.title")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Log file (*.log)", "*.log"), - new FileChooser.ExtensionFilter("All files", "*.*")); - - Path hs_err_path = crashList.getSelectionModel().getSelectedItem().getHsErrFile().toPath(); - Optional.ofNullable(dialog.showSaveDialog(MainWindowController.getInstance().getOwner())) - .ifPresent(new ConsumerWrapper<>(t -> Files.copy(hs_err_path, t.toPath(), StandardCopyOption.REPLACE_EXISTING))); - } - - private void showJdpDecoderDetail(JdpDecoder data){ - detailTable.setItems(data.jdpTableKeyValueProperty().get()); - } - - - @Override - public String getPluginName() { - return "JVMLive"; - } - - @Override - public String getLicense() { - return PluginController.LICENSE_GPL_V2; - } - - @Override - public Map getLibraryLicense() { - return null; - } - - @Override - public EventHandler getOnPluginTabSelected() { - // do nothing - return null; - } - - private void closeJMXConnection(Object obj){ - if(obj instanceof JMXHelper){ - try{ - ((JMXHelper)obj).close(); - } catch(ConnectException ex){ - // Do nothing - // Runtime will connect remote host when JMX connection will be closed. - // So we can skip this operation. - Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - } - } - - @Override - public Runnable getOnCloseRequest() { - return () -> { - Optional.ofNullable(jdpValidator).ifPresent(p -> p.cancel()); - Optional.ofNullable(jdpReceiver).ifPresent(r -> r.cancel(true)); - Optional.ofNullable(receiverThread).ifPresent(new ConsumerWrapper<>(t -> t.join())); - errorReportServer.cancel(true); - try{ - errorReportThread.join(); - } - catch (InterruptedException ex){ - Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); - } - - taskThreadPool.shutdownNow(); - try { - taskThreadPool.awaitTermination(JVMLiveConfig.getThreadpoolShutdownAwaitTime(), TimeUnit.SECONDS); - } catch (InterruptedException ex) { - Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); - } - - jmxThreadPool.shutdownNow(); - try { - jmxThreadPool.awaitTermination(JVMLiveConfig.getThreadpoolShutdownAwaitTime(), TimeUnit.SECONDS); - } catch (InterruptedException ex) { - Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); - } - - jvmList.getItems().forEach(p -> closeJMXConnection(p.getHeapStatsTableKeyValue().valueProperty().get())); - }; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/errorreporter/ErrorReportDecoder.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/errorreporter/ErrorReportDecoder.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2014-2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter; - -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.SeekableByteChannel; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.StandardOpenOption; -import java.time.LocalDateTime; -import java.util.concurrent.Future; -import java.util.logging.Level; -import java.util.logging.Logger; -import javafx.application.Platform; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; - -/** - * Error report decoder. - * This class can treat data which is generated by -XX:+TransmitErrorReport and -XX:ErrorReportServer=<address>:<port> . - * - * @author Yasumasa Suenaga - */ -public class ErrorReportDecoder extends Task{ - - /** Buffer size for receive buffer. */ - public static final int BUFFER_SIZE = 1024; - - /** - * Created time of this instance. - * This time equals JVM crashed. - */ - private LocalDateTime crashedTime; - - /** IP address for crash JVM. */ - private InetAddress localIP; - - /** User name for crash Java process. */ - private String user; - - private boolean crash; - - private boolean debug; - - private int procCount; - - private int buildVer; - - private int[] resolveTiming; - - private int resolvePlatform; - - /** JDK version for crash JVM. */ - private String jdkVersion; - - /** VM version for crash JVM. */ - private String vmVersion; - - /** Length of this error report. */ - private int reportLength; - - /** - * File instance for this hs_err report. - * This file is temporary. So we add "deleteOnExit" to this field. - */ - private File hsErrFile; - - private final ObservableList crashList; - - /** Async channel to crash JVM. */ - private final AsynchronousSocketChannel ch; - - /** InetSocketAddress for crash JVM. */ - private final InetSocketAddress sockAddr; - - /** - * Constructor of ErrorReportDecorder. - * - * @param crashList List of crash jvms. - * @param ch AsynchronousSocketChannel to crash jvm. - * - * @throws IOException If an I/O error occurs - */ - public ErrorReportDecoder(ObservableList crashList, AsynchronousSocketChannel ch) throws IOException{ - crashedTime = LocalDateTime.now(); - - localIP = null; - user = null; - crash = false; - debug = false; - procCount = -1; - buildVer = -1; - resolveTiming = new int[2]; - resolvePlatform = -1; - jdkVersion = null; - vmVersion = null; - reportLength = -1; - hsErrFile = null; - - this.crashList = crashList; - this.ch = ch; - this.sockAddr = (InetSocketAddress)ch.getRemoteAddress(); - } - - /** - * Parse header information in error report data. - * - * @param buf Received data - */ - private void parseHeader(ByteBuffer buf){ - - /* Check magic number */ - if(buf.getInt() != 0xcafebabe){ - throw new IllegalArgumentException("Magic number of ErrorReport is incorrected: from " + sockAddr.toString()); - } - - /* Get unknown data 1 (do nothing) */ - buf.getInt(); - - /* IP address which error is occurred */ - byte[] rawIP = new byte[4]; - buf.get(rawIP); - try { - localIP = InetAddress.getByAddress(rawIP); - } catch (UnknownHostException ex) { - Logger.getLogger(ErrorReportServer.class.getName()).log(Level.SEVERE, null, ex); - } - - /* User name */ - byte[] rawUserName = new byte[32]; - buf.get(rawUserName); - user = new String(rawUserName, StandardCharsets.UTF_8); - - /* Flags */ - int flags = buf.getInt(); - procCount = flags & 0x07; - buildVer = (flags >> 0x05) & 0x3; - if((flags & 0x80) != 0) crash = true; - if((flags & 0x03) != 0) debug = true; - - /* Resolve timing */ - resolveTiming[0] = buf.getInt(); - resolveTiming[1] = buf.getInt(); - - /* Resolve platform */ - resolvePlatform = buf.getInt(); - - /* Get unknown data 2 (do nothing) */ - buf.getInt(); - - /* Get unknown data 3 (do nothing) */ - buf.getInt(); - - /* Get unknown data 4 (do nothing) */ - buf.getInt(); - - /* Get unknown data 5 (do nothing) */ - buf.getInt(); - - /* Get unknown data 6 (do nothing) */ - buf.getInt(); - - /* Get unknown data 7 (do nothing) */ - buf.getInt(); - - /* Get unknown data 8 (do nothing) */ - buf.getInt(); - - /* Get unknown data 9 (do nothing) */ - buf.getInt(); - - /* Get unknown data 10 (do nothing) */ - buf.getInt(); - - /* JDK version */ - byte[] rawJDKVersion = new byte[buf.getInt()]; - buf.get(rawJDKVersion); - jdkVersion = new String(rawJDKVersion, StandardCharsets.UTF_8); - - /* VM version */ - byte[] rawVMVersion = new byte[buf.getInt()]; - buf.get(rawVMVersion); - vmVersion = new String(rawVMVersion, StandardCharsets.UTF_8); - - /* Length of hs_err report */ - reportLength = buf.getInt(); - } - - public InetAddress getLocalIP() { - return localIP; - } - - public void setLocalIP(InetAddress localIP) { - this.localIP = localIP; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public void setCrash(){ - this.crash = true; - } - - public boolean isCrash(){ - return this.crash; - } - - public void setDebug(){ - this.debug = true; - } - - public boolean isDebug(){ - return this.debug; - } - - public int getProcCount() { - return procCount; - } - - public void setProcCount(int procCount) { - this.procCount = procCount; - } - - public int getBuildVer() { - return buildVer; - } - - public void setBuildVer(int buildVer) { - this.buildVer = buildVer; - } - - public int[] getResolveTiming() { - return resolveTiming; - } - - public void setResolveTiming(int[] resolveTiming) { - this.resolveTiming = resolveTiming; - } - - public int getResolvePlatform() { - return resolvePlatform; - } - - public void setResolvePlatform(int resolvePlatform) { - this.resolvePlatform = resolvePlatform; - } - - public String getJdkVersion() { - return jdkVersion; - } - - public void setJdkVersion(String jdkVersion) { - this.jdkVersion = jdkVersion; - } - - public String getVmVersion() { - return vmVersion; - } - - public void setVmVersion(String vmVersion) { - this.vmVersion = vmVersion; - } - - public int getReportLength() { - return reportLength; - } - - public void setReportLength(int reportLength) { - this.reportLength = reportLength; - } - - public File getHsErrFile() { - return hsErrFile; - } - - public void setHsErrFile(File hsErrFile) { - this.hsErrFile = hsErrFile; - } - - public LocalDateTime getCrashedTime() { - return crashedTime; - } - - public void setCrashedTime(LocalDateTime crashedTime) { - this.crashedTime = crashedTime; - } - - @Override - public String toString() { - return localIP.toString(); - } - - @Override - protected Void call() throws Exception { - - try{ - ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE); - Future bytes = ch.read(buf); - - if(bytes.get() <= 0){ - return null; - } - - buf.flip(); - parseHeader(buf); - - if(reportLength > 0){ - int remaining = reportLength; - File hs_err_log = File.createTempFile("hs_err", sockAddr.getAddress().getHostAddress()); - hs_err_log.deleteOnExit(); - - try(SeekableByteChannel hs_err = Files.newByteChannel(hs_err_log.toPath(), - StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)){ - - while(remaining > 0){ - int sizeShouldWrite = buf.limit() - buf.position(); - if(sizeShouldWrite > remaining){ - sizeShouldWrite = remaining; - buf.limit(sizeShouldWrite); - } - remaining -= sizeShouldWrite; - - hs_err.write(buf); - buf.flip(); - ch.read(buf).get(); - buf.flip(); - } - - } - - hsErrFile = hs_err_log; - Platform.runLater(() -> crashList.add(this)); - } - - } - finally{ - ch.close(); - } - - return null; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/errorreporter/ErrorReportServer.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/errorreporter/ErrorReportServer.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2014-2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter; - -import java.net.InetSocketAddress; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; - -/** - * Service thread to receive crash reports. - * This thread can treat data which is generated by -XX:+TransmitErrorReport and -XX:ErrorReportServer=<address>:<port> . - * - * @author Yasumasa Suenaga - */ -public class ErrorReportServer extends Task{ - - public static final int DEFAULT_ERROR_REPORT_SERVER_PORT = 4711; - - private final int port; - - private final ObservableList crashList; - - private final ExecutorService hsErrProcPool; - - /** - * Constructor of ErrorReportServer. - * - * @param crashList List of crash jvms. - * @param hsErrProcPool ThreadPool which processes ErrorReport. - */ - public ErrorReportServer(ObservableList crashList, ExecutorService hsErrProcPool){ - this(DEFAULT_ERROR_REPORT_SERVER_PORT, crashList, hsErrProcPool); - } - - /** - * Constructor of ErrorReportServer. - * @param port Port number of ErrorReportServer. - * @param crashList List of crash jvms. - * @param hsErrProcPool ThreadPool which processes ErrorReport. - */ - public ErrorReportServer(int port, ObservableList crashList, ExecutorService hsErrProcPool){ - this.port = port; - this.crashList = crashList; - this.hsErrProcPool = hsErrProcPool; - } - - @Override - protected Void call() throws Exception { - - try(AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open()){ - server.bind(new InetSocketAddress(port)); - - while(!isCancelled()){ - Future sock = server.accept(); - hsErrProcPool.submit(new ErrorReportDecoder(crashList, sock.get())); - } - - } - - return null; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpDecoder.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpDecoder.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2014-2016 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp; - -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import javafx.application.Platform; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Label; -import javafx.scene.control.Labeled; -import javafx.scene.control.ListView; -import jp.co.ntt.oss.heapstats.fx.lambda.EventHandlerWrapper; -import jp.co.ntt.oss.heapstats.jmx.JMXHelper; -import jp.co.ntt.oss.heapstats.lambda.RunnableWrapper; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - - -/** - * JDP packet decoder. - * This class containes JDP packet data and method for parse it. - * - * @author Yasumasa Suenaga - */ -public class JdpDecoder extends Task{ - - /** Key of JDP for main class. */ - public static final String MAIN_CLASS_KEY = "MAIN_CLASS"; - - /** Key of JDP for instance name. */ - public static final String INSTANCE_NAME_KEY = "INSTANCE_NAME"; - - /** Key of JDP for JMX URL. */ - public static final String JMX_SERVICE_URL_KEY = "JMX_SERVICE_URL"; - - /** Key of JDP for UUID. */ - public static final String DISCOVERABLE_SESSION_UUID_KEY = "DISCOVERABLE_SESSION_UUID"; - - /** Key of JDP for broadcast interval. */ - public static final String PACKET_BROADCAST_INTERVAL_KEY = "BROADCAST_INTERVAL"; - - /** Key of JDP for PID. */ - public static final String PROCESS_ID_KEY = "PROCESS_ID"; - - /** Raw JDP packet data. */ - private final ByteBuffer jdpRawData; - - /** - * Received time of this instance. - */ - private LocalDateTime receivedTime; - - /** Source address of this JDP packet. */ - private final InetSocketAddress sourceAddr; - - /** Main class of this JDP packet. */ - private String mainClass; - - /** Instance name of this JDP packet. */ - private String instanceName; - - /** JMX URL of this JDP packet. */ - private String jmxServiceURL; - - /** PID of this JDP packet. */ - private int pid; - - /** UUID of this JDP packet. */ - private UUID uuid; - - /** Broadcast interval of this JDP packet. */ - private int broadcastInterval; - - /** If this packet is expired, this flag is set to true by JdpValidatorService. */ - private final BooleanProperty invalidate; - - private final ObjectProperty> jdpTableKeyValue; - - private final ListView jdpList; - - private final Optional jconsolePath; - - private final ExecutorService jmxProcPool; - - private JdpTableKeyValue heapstatsValue; - - /** - * Constructor of JdpDecorder. - * - * @param sourceAddr Source address of JDP packet. - * @param rawData JDP raw data. - * @param jdpList ListView which includes JDP. - * @param jconsolePath Path to JConsole. - * @param jmxProcPool ThreadPool which processes JMX access. - */ - public JdpDecoder(InetSocketAddress sourceAddr, ByteBuffer rawData, ListView jdpList, Optional jconsolePath, ExecutorService jmxProcPool){ - this.receivedTime = LocalDateTime.now(); - this.sourceAddr = sourceAddr; - this.jdpRawData = rawData; - this.jdpList = jdpList; - this.invalidate = new SimpleBooleanProperty(false); - this.jdpTableKeyValue = new SimpleObjectProperty<>(); - this.jconsolePath = jconsolePath; - this.jmxProcPool = jmxProcPool; - } - - /** - * Create String instance from ByteBuffer. - * - * @param buffer ByteBuffer to parse. Position must be set to top of String data. - * @return String from ByteBuffer. - */ - private String getStringFromByteBuffer(ByteBuffer buffer){ - int length = Short.toUnsignedInt(buffer.getShort()); - byte[] rawValue = new byte[length]; - buffer.get(rawValue); - - return new String(rawValue, StandardCharsets.UTF_8); - } - - private Map parseJdpPacket(){ - - if(jdpRawData.getInt() != 0xC0FFEE42){ - throw new RuntimeException("Invalid magic number."); - } - - if(jdpRawData.getShort() != 1){ - throw new RuntimeException("Invalid JDP version."); - } - - Map jdpContents = new HashMap<>(); - - while(jdpRawData.hasRemaining()){ - String key = getStringFromByteBuffer(jdpRawData); - String value = getStringFromByteBuffer(jdpRawData); - - jdpContents.put(key, value); - } - - return jdpContents; - } - - private void setJdpTableKeyValue(JdpTableKeyValue val, Labeled jmxURL){ - switch(val.keyProperty().get()){ - case "Received Time": - val.valueProperty().set(receivedTime.format(HeapStatsUtils.getDateTimeFormatter())); - break; - - case "Address": - val.valueProperty().set(sourceAddr.getAddress().getHostAddress()); - break; - - case "JDP Instance Name": - val.valueProperty().set(instanceName); - break; - - case "Main Class": - val.valueProperty().set(mainClass); - break; - - case "UUID": - val.valueProperty().set(uuid.toString()); - break; - - case "PID": - val.valueProperty().set(Integer.toString(pid)); - break; - - case "JMX URL": - val.valueProperty().set(jmxURL); - } - } - - /** - * Update JDP packet if same UUID is registered in JDP list. - */ - private void updateJDPData(){ - Labeled jmxURL; - - if(jconsolePath.isPresent()){ - String[] execParam = {jconsolePath.get(), jmxServiceURL}; - jmxURL = new Hyperlink(jmxServiceURL); - ((Hyperlink)jmxURL).setOnAction(new EventHandlerWrapper<>(e -> Runtime.getRuntime().exec(execParam))); - } - else{ - jmxURL = new Label(jmxServiceURL); - } - - int idx = jdpList.getItems().indexOf(this); - - if(idx == -1){ - heapstatsValue = new JdpTableKeyValue("HeapStats", "Checking..."); - jmxProcPool.submit(new RunnableWrapper(() -> heapstatsValue.valueProperty().set(new JMXHelper(jmxServiceURL)))); - - jdpTableKeyValue.set(FXCollections.observableArrayList( - new JdpTableKeyValue("Received Time", receivedTime.format(HeapStatsUtils.getDateTimeFormatter())), - new JdpTableKeyValue("Address", sourceAddr.getAddress().getHostAddress()), - new JdpTableKeyValue("JDP Instance Name", instanceName), - new JdpTableKeyValue("Main Class", mainClass), - new JdpTableKeyValue("UUID", uuid.toString()), - new JdpTableKeyValue("PID", Integer.toString(pid)), - new JdpTableKeyValue("JMX URL", jmxURL), - heapstatsValue - )); - jdpList.getItems().add(this); - } - else{ - JdpDecoder existData = jdpList.getItems().get(idx); - existData.receivedTime = receivedTime; - existData.jdpTableKeyValue.get().forEach(p -> setJdpTableKeyValue(p, jmxURL)); - } - - } - - @Override - protected Void call() throws Exception { - Map jdpData = parseJdpPacket(); - - this.mainClass = jdpData.get(MAIN_CLASS_KEY); - this.instanceName = jdpData.get(INSTANCE_NAME_KEY); - this.jmxServiceURL = jdpData.get(JMX_SERVICE_URL_KEY); - this.uuid = UUID.fromString(jdpData.get(DISCOVERABLE_SESSION_UUID_KEY)); - this.pid = Integer.parseInt(jdpData.get(PROCESS_ID_KEY)); - this.broadcastInterval = Integer.parseInt(jdpData.get(PACKET_BROADCAST_INTERVAL_KEY)); - - Platform.runLater(this::updateJDPData); - - return null; - } - - /** - * Get time of this JDP packet was received. - * - * @return Received time. - */ - public LocalDateTime getReceivedTime() { - return receivedTime; - } - - /** - * Get source address of JDP packet. - * - * @return Source address of JDP. - */ - public InetSocketAddress getSourceAddr() { - return sourceAddr; - } - - /** - * Get main class in JDP packet. - * - * @return Main class. - */ - public String getMainClass() { - return mainClass; - } - - /** - * Get JMX URL in JDP packet. - * - * @return JDP URL. - */ - public String getJmxServiceURL() { - return jmxServiceURL; - } - - /** - * Get PID in JDP packet. - * @return PID - */ - public int getPid() { - return pid; - } - - /** - * Get UUID in JDP packet. - * @return UUID - */ - public UUID getUuid() { - return uuid; - } - - /** - * Get instance name in JDP packet. - * @return Instance name. - */ - public String getInstanceName() { - return instanceName; - } - - /** - * Get broadcast interval in JDP packet. - * - * @return JDP broadcast interval. - */ - public int getBroadcastInterval() { - return broadcastInterval; - } - - /** - * Set invalidate to this JDP packet. - */ - public void setInvalidate(){ - invalidate.set(true); - } - - /** - * Get invalidate property. - * - * @return Invalidate property. - */ - public BooleanProperty invalidateProperty(){ - return this.invalidate; - } - - /** - * Get JDP packet data list. - * This method returns key-value list. - * - * @return JDP packet data list. - */ - public ObjectProperty> jdpTableKeyValueProperty(){ - return this.jdpTableKeyValue; - } - - /** - * Get JDP Key-Value. - * - * @return JDP packet data. - */ - public JdpTableKeyValue getHeapStatsTableKeyValue(){ - return this.heapstatsValue; - } - - @Override - public int hashCode() { - return this.uuid.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - return this.uuid.equals(((JdpDecoder)obj).uuid); - } - - @Override - public String toString() { - return Optional.ofNullable(instanceName).orElse(mainClass); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpReceiver.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpReceiver.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2014-2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.SocketAddress; -import java.net.StandardProtocolFamily; -import java.net.StandardSocketOptions; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedByInterruptException; -import java.nio.channels.DatagramChannel; -import java.util.Collections; -import java.util.Optional; -import java.util.concurrent.ExecutorService; -import javafx.concurrent.Task; -import javafx.scene.control.ListView; -import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; -import jp.co.ntt.oss.heapstats.lambda.PredicateWrapper; - -/** - * JDP Packet receiver thread. - * - * @author Yasumasa Suenaga - */ -public class JdpReceiver extends Task{ - - /** System property key of JDP multicast address. */ - public static final String JDP_ADDRESS_PROP_NAME = "com.sun.management.jdp.address"; - - /** System property key of JDP port, */ - public static final String JDP_PORT_PROP_NAME = "com.sun.management.jdp.port"; - - /** Default JDP multicast address. */ - public static final String JDP_DEFAULT_ADDRESS = "224.0.23.178"; - - /** Default JDP port. */ - public static final int JDP_DEFAULT_PORT = 7095; - - /** UDP packet length. */ - public static final int UDP_PACKET_LENGTH = 65535; // 64KB - - private InetAddress jdpAddr; - - private int jdpPort; - - /** Thread pool for JdpDecoder. */ - private ExecutorService jdpProcPool; - - private ExecutorService jmxProcPool; - - private ListView jdpList; - - private Optional jconsolePath; - - /** - * Constructor of JdpReceiver. - * - * @param jdpAddr JDP multicast address. - * @param jdpPort JDP port. - * @param jdpList ListView which includes JDP packet data. - * @param threadPool ThreadPool which processes JDP packet decording. - * @param jconsolePath Path to JConsole. - * @param jmxPool ThreadPool which processes JMX access. - */ - public JdpReceiver(InetAddress jdpAddr, int jdpPort, ListView jdpList, ExecutorService threadPool, Optional jconsolePath, ExecutorService jmxPool){ - this.jdpAddr = jdpAddr; - this.jdpPort = jdpPort; - this.jdpProcPool = threadPool; - this.jdpList = jdpList; - this.jconsolePath = jconsolePath; - this.jmxProcPool = jmxPool; - } - - /** - * Constructor of JdpReceiver. - * - * @param jdpList ListView which includes JDP packet data. - * @param threadPool ThreadPool which processes JDP packet decording. - * @param jconsolePath Path to JConsole. - * @param jmxPool ThreadPool which processes JMX access. - * - * @throws UnknownHostException Invalid host information to access. - */ - public JdpReceiver(ListView jdpList, ExecutorService threadPool, Optional jconsolePath, ExecutorService jmxPool) throws UnknownHostException{ - this(InetAddress.getByName(Optional.ofNullable(System.getProperty(JDP_ADDRESS_PROP_NAME)).orElse(JDP_DEFAULT_ADDRESS)), - Optional.ofNullable(System.getProperty(JDP_PORT_PROP_NAME)).map(p -> Integer.parseInt(p)).orElse(JDP_DEFAULT_PORT), - jdpList, threadPool, jconsolePath, jmxPool); - } - - @Override - protected Void call() throws Exception { - - try(DatagramChannel jdpChannel = DatagramChannel.open(StandardProtocolFamily.INET)){ - jdpChannel.bind(new InetSocketAddress(jdpPort)); - jdpChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - - Collections.list(NetworkInterface.getNetworkInterfaces()).stream() - .filter(i -> i.getInterfaceAddresses().stream() - .anyMatch(n -> n.getAddress() instanceof Inet4Address)) - .filter(new PredicateWrapper<>(i -> i.supportsMulticast())) - .peek(new ConsumerWrapper<>(i ->jdpChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, i))) - .forEach(new ConsumerWrapper<>(i -> jdpChannel.join(jdpAddr, i))); - - while(!isCancelled()){ - ByteBuffer buf = ByteBuffer.allocateDirect(UDP_PACKET_LENGTH); - SocketAddress src = jdpChannel.receive(buf); - - buf.flip(); - jdpProcPool.submit(new JdpDecoder((InetSocketAddress)src, buf, this.jdpList, jconsolePath, jmxProcPool)); - } - - } - catch(ClosedByInterruptException ex){ - // Do nothing. - // This exception may be occurred by thread interruption. - } - - return null; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpTableKeyValue.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpTableKeyValue.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp; - -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; - -/** - * JDP key and value which is used in TableView. - * - * @author Yasumasa Suenaga - */ -public class JdpTableKeyValue { - - private final StringProperty key; - - private final ObjectProperty value; - - /** - * Constructor of JdpTableKeyValue. - * - * @param key JDP key. - * @param value JDP value. - */ - public JdpTableKeyValue(String key, Object value){ - this.key = new SimpleStringProperty(key); - this.value = new SimpleObjectProperty<>(value); - } - - /** - * JDP key property. - * @return Key property. - */ - public StringProperty keyProperty(){ - return key; - } - - /** - * JDP value property. - * @return Value property. - */ - public ObjectProperty valueProperty(){ - return value; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpValidatorService.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/jdp/JdpValidatorService.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2014-2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp; - -import java.time.LocalDateTime; -import javafx.application.Platform; -import javafx.concurrent.ScheduledService; -import javafx.concurrent.Task; -import javafx.scene.control.ListView; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.JVMLiveConfig; - -/** - * Validation service class for JdpDecoder. - * This class should be run on ScheduledService. - * - * The task which is provided from this class checkes all JdpDecoder in ListView. - * If JDP Packet is not received until JDP packet interval, the Task change its - * color to orange. - * - * @author Yasumasa Suenaga - */ -public class JdpValidatorService extends ScheduledService{ - - private final ListView jdpList; - - public JdpValidatorService(ListView jdpList) { - this.jdpList = jdpList; - } - - @Override - protected Task createTask() { - return new JdpValidationTask(); - } - - class JdpValidationTask extends Task{ - - private void swapJdpDecoder(int index){ - JdpDecoder target = jdpList.getItems().get(index); - target.setInvalidate(); - - jdpList.getItems().remove(index); - jdpList.getItems().add(index, target); - } - - @Override - protected Void call() throws Exception { - Platform.runLater(() -> { - LocalDateTime now = LocalDateTime.now(); - jdpList.getItems().stream() - .filter(d -> !d.invalidateProperty().get()) - .filter(d -> d.getReceivedTime().plusSeconds(d.getBroadcastInterval() / 1000 + JVMLiveConfig.getJdpWaitDuration()).isBefore(now)) - .mapToInt(d -> jdpList.getItems().indexOf(d)) - .forEach(i -> swapJdpDecoder(i)); - }); - - return null; - } - - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsConfig.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsConfig.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2014-2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.mbean; - -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.scene.control.Control; - -/** - * Container class of HeapStats agent configuration. - * - * @author Yasumasa Suenaga - */ -public class HeapStatsConfig { - - private final StringProperty key; - - private final Property value; - - private final Object currentValue; - - private Control cellContent; - - private final BooleanProperty changed; - - /** - * Constructor of HeapStatsConfig. - * - * @param key Configuration key. - * @param value Configuration value. - */ - @SuppressWarnings("unchecked") - public HeapStatsConfig(String key, Object value){ - this.key = new SimpleStringProperty(key); - changed = new SimpleBooleanProperty(); - - if(value == null){ - this.value = new SimpleStringProperty(null); - this.currentValue = null; - } - else if(value instanceof Boolean){ - this.value = new SimpleBooleanProperty((Boolean)value); - this.currentValue = (Boolean)value; - } - else if(value instanceof Long){ - this.value = new SimpleLongProperty((Long)value); - this.currentValue = (Long)value; - } - else if(value instanceof String){ - this.value = new SimpleStringProperty((String)value); - this.currentValue = (String)value; - } - else{ - this.value = new SimpleObjectProperty<>(value); - this.currentValue = value; - } - - this.value.addListener((v, o, n) -> changed.set(!n.equals(currentValue))); - this.cellContent = null; - } - - /** - * Get configuration key. - * - * @return Configuration key. - */ - public StringProperty keyProperty(){ - return key; - } - - /** - * Get configuration value. - * - * @return Configuration value. - */ - public Property valueProperty(){ - return value; - } - - /** - * Get change property. - * true if this configuration is changed. - * - * @return Change property. - */ - public BooleanProperty changedProperty(){ - return changed; - } - - /** - * Get table cell control of this configuration. - * - * @return Control in TableCell. - */ - public Control getCellContent() { - return cellContent; - } - - /** - * Set table cell control of this configuration. - * - * @param cellContent New control in TableCell. - */ - public void setCellContent(Control cellContent) { - this.cellContent = cellContent; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.mbean; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.Locale; -import java.util.ResourceBundle; -import java.util.concurrent.ExecutionException; -import javafx.beans.binding.Bindings; -import javafx.beans.property.Property; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.ButtonType; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.Control; -import javafx.scene.control.Label; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.stage.FileChooser; -import javafx.util.converter.IntegerStringConverter; -import javafx.util.converter.LongStringConverter; -import jp.co.ntt.oss.heapstats.MainWindowController; -import jp.co.ntt.oss.heapstats.jmx.JMXHelper; -import jp.co.ntt.oss.heapstats.mbean.HeapStatsMBean; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class of HeapStatsMBean. - * - * @author Yasumasa Suenaga - */ -public class HeapStatsMBeanController implements Initializable { - - @FXML - private Label headerLabel; - - @FXML - TableView configTable; - - @FXML - TableColumn keyColumn; - - @FXML - TableColumn valueColumn; - - private JMXHelper jmxHelper; - - - private static class ConfigKeyTableCell extends TableCell{ - - @Override - protected void updateItem(String item, boolean empty) { - super.updateItem(item, empty); - - if(!empty){ - HeapStatsConfig config = (HeapStatsConfig)getTableRow().getItem(); - styleProperty().bind(Bindings.createStringBinding(() -> config.changedProperty().get() ? "-fx-text-fill: orange;" : "-fx-text-fill: black;", config.changedProperty())); - setText(item); - } - - } - - } - - private static class VariousTableCell extends TableCell{ - - @Override - @SuppressWarnings("unchecked") - protected void updateItem(Object item, boolean empty) { - super.updateItem(item, empty); - - if(empty){ - return; - } - - HeapStatsConfig config = (HeapStatsConfig)getTableRow().getItem(); - if(config == null){ - return; - } - - Control cellContent = config.getCellContent(); - if(item instanceof Boolean){ - if(!(cellContent instanceof CheckBox)){ - cellContent = new CheckBox(); - ((CheckBox)cellContent).selectedProperty().bindBidirectional(config.valueProperty()); - } - } - else if(item instanceof HeapStatsMBean.LogLevel){ - if(!(cellContent instanceof ChoiceBox)){ - cellContent = new ChoiceBox(FXCollections.observableArrayList(HeapStatsMBean.LogLevel.values())); - ((ChoiceBox)cellContent).valueProperty().bindBidirectional(config.valueProperty()); - } - } - else if(item instanceof HeapStatsMBean.RankOrder){ - if(!(cellContent instanceof ChoiceBox)){ - cellContent = new ChoiceBox(FXCollections.observableArrayList(HeapStatsMBean.RankOrder.values())); - ((ChoiceBox)cellContent).valueProperty().bindBidirectional(config.valueProperty()); - } - } - else if(item instanceof Integer){ - if(!(cellContent instanceof TextField)){ - cellContent = new TextField(); - ((TextField)cellContent).textProperty().bindBidirectional((Property)config.valueProperty(), new IntegerStringConverter()); - } - } - else if(item instanceof Long){ - if(!(cellContent instanceof TextField)){ - cellContent = new TextField(); - ((TextField)cellContent).textProperty().bindBidirectional((Property)config.valueProperty(), new LongStringConverter()); - } - } - else{ - if(!(cellContent instanceof TextField)){ - cellContent = new TextField(); - ((TextField)cellContent).textProperty().bindBidirectional(config.valueProperty()); - } - } - - config.setCellContent(cellContent); - setGraphic(cellContent); - } - - } - - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - keyColumn.setCellValueFactory(new PropertyValueFactory<>("key")); - keyColumn.setCellFactory(p -> new ConfigKeyTableCell()); - valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); - valueColumn.setCellFactory(p -> new VariousTableCell()); - } - - /** - * Get JMXHelper instance. - * - * @return Instance of JMXHelper. - */ - public JMXHelper getJmxHelper() { - return jmxHelper; - } - - /** - * Set JMXHelper instance. - * - * @param jmxHelper New JMXHelper instance. - */ - public void setJmxHelper(JMXHelper jmxHelper) { - this.jmxHelper = jmxHelper; - } - - /** - * Load all configs from remote HeapStats agent through JMX. - */ - public void loadAllConfigs(){ - headerLabel.setText(jmxHelper.getUrl().toString()); - configTable.setItems(jmxHelper.getMbean().getConfigurationList() - .entrySet() - .stream() - .map(e -> new HeapStatsConfig(e.getKey(), e.getValue())) - .collect(FXCollections::observableArrayList, ObservableList::add, ObservableList::addAll)); - } - - @FXML - private void onCommitBtnClick(ActionEvent event){ - - try{ - configTable.getItems().stream() - .filter(c -> c.changedProperty().get()) - .forEach(c -> jmxHelper.getMbean().changeConfiguration(c.keyProperty().get(), c.valueProperty().getValue())); - } - catch(IllegalArgumentException e){ - HeapStatsUtils.showExceptionDialog(e); - return; - } - - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.applyConfig"), ButtonType.OK); - dialog.show(); - - loadAllConfigs(); - } - - @FXML - private void onInvokeResourceBtnClick(ActionEvent event){ - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - - if(jmxHelper.getMbean().invokeLogCollection()){ - Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.invoke.resource.success"), ButtonType.OK); - dialog.show(); - } - else{ - Alert dialog = new Alert(AlertType.ERROR, resource.getString("dialog.config.message.invoke.resource.fail"), ButtonType.OK); - dialog.show(); - } - } - - @FXML - private void onInvokeAllBtnClick(ActionEvent event){ - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - - if(jmxHelper.getMbean().invokeAllLogCollection()){ - Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.invoke.archive.success"), ButtonType.OK); - dialog.show(); - } - else{ - Alert dialog = new Alert(AlertType.ERROR, resource.getString("dialog.config.message.invoke.archive.fail"), ButtonType.OK); - dialog.show(); - } - } - - @FXML - private void onInvokeSnapShotBtnClick(ActionEvent event){ - jmxHelper.getMbean().invokeSnapShotCollection(); - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - Alert dialog = new Alert(AlertType.INFORMATION, resource.getString("dialog.config.message.invoke.snapshot"), ButtonType.OK); - dialog.show(); - } - - @FXML - private void onGetResourceBtnClick(ActionEvent event){ - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - dialog.setTitle(resource.getString("dialog.config.resource.save")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("CSV file (*.csv)", "*.csv"), - new FileChooser.ExtensionFilter("All files", "*.*")); - File logFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); - - if(logFile != null){ - try { - jmxHelper.getResourceLog(logFile.toPath()); - } catch (IOException | InterruptedException | ExecutionException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - } - - } - - @FXML - private void onGetSnapShotBtnClick(ActionEvent event){ - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); - dialog.setTitle(resource.getString("dialog.config.snapshot.save")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("SnapShot file (*.dat)", "*.dat"), - new FileChooser.ExtensionFilter("All files", "*.*")); - File snapshotFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); - - if(snapshotFile != null){ - try { - jmxHelper.getSnapShot(snapshotFile.toPath()); - } catch (IOException | InterruptedException | ExecutionException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - } - - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/ArchiveDataConverter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/ArchiveDataConverter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2014-2016 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package jp.co.ntt.oss.heapstats.plugin.builtin.log; - -import java.util.Optional; -import javafx.util.StringConverter; -import jp.co.ntt.oss.heapstats.container.log.ArchiveData; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * This class converts LocalDateTime in ArchiveData to String. - * This class DO NOT support fromString() method. - * - * @author Yasumasa Suenaga - */ -public class ArchiveDataConverter extends StringConverter{ - - @Override - public String toString(ArchiveData object) { - return Optional.ofNullable(object) - .map(o -> o.getDate().format(HeapStatsUtils.getDateTimeFormatter())) - .orElse(null); - } - - @Override - public ArchiveData fromString(String string) { - throw new UnsupportedOperationException("ArchiveData DO NOT convert from String."); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.log; - -import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; -import javafx.event.ActionEvent; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.SplitPane; -import javafx.scene.control.TextField; -import javafx.stage.FileChooser; -import javafx.stage.FileChooser.ExtensionFilter; -import jp.co.ntt.oss.heapstats.MainWindowController; -import jp.co.ntt.oss.heapstats.container.log.ArchiveData; -import jp.co.ntt.oss.heapstats.container.log.DiffData; -import jp.co.ntt.oss.heapstats.container.log.LogData; -import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; -import jp.co.ntt.oss.heapstats.lambda.FunctionWrapper; -import jp.co.ntt.oss.heapstats.plugin.PluginController; -import jp.co.ntt.oss.heapstats.plugin.builtin.log.tabs.LogDetailsController; -import jp.co.ntt.oss.heapstats.plugin.builtin.log.tabs.LogResourcesController; -import jp.co.ntt.oss.heapstats.task.ParseLogFile; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; -import jp.co.ntt.oss.heapstats.utils.TaskAdapter; - -import java.io.File; -import java.net.URL; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.stream.Collectors; - -/** - * FXML Controller of LOG builtin plugin. - * - * @author Yasumasa Suenaga - */ -public class LogController extends PluginController implements Initializable { - - @FXML - private LogResourcesController logResourcesController; - - @FXML - private LogDetailsController logDetailsController; - - @FXML - private Label startTimeLabel; - - @FXML - private Label endTimeLabel; - - @FXML - private TextField logFileList; - - @FXML - private Button okBtn; - - @FXML - private SplitPane rangePane; - - private ObjectProperty rangeStart; - - private ObjectProperty rangeEnd; - - private List logEntries; - - private List diffEntries; - - private ObjectProperty> archiveList; - - /** - * Update caption of label which represents time of selection. - * - * @param target Label compornent to draw. - * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 . - */ - private void updateRangeLabel(Label target, double newValue){ - if(!Optional.ofNullable(logEntries).map(List::isEmpty).orElse(true)){ - LocalDateTime start = logEntries.get(0).getDateTime(); - LocalDateTime end = logEntries.get(logEntries.size() - 1).getDateTime(); - long diff = start.until(end, ChronoUnit.MILLIS); - LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS); - - if(target == startTimeLabel){ - rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS)); - } - else{ - rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS)); - } - - target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter())); - } - } - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - super.initialize(url, rb); - - logEntries = null; - diffEntries = null; - rangeStart = new SimpleObjectProperty<>(); - rangeEnd = new SimpleObjectProperty<>(); - - rangePane.getDividers().get(0).positionProperty().addListener((v, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue())); - rangePane.getDividers().get(1).positionProperty().addListener((v, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue())); - - archiveList = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); - logResourcesController.archiveListProperty().bind(archiveList); - logDetailsController.archiveListProperty().bind(archiveList); - setOnWindowResize((v, o, n) -> Platform.runLater(logResourcesController::drawEventLineToChart)); - } - - /** - * onSucceeded event handler for LogFileParser. - * - * @param parser Targeted LogFileParser. - */ - private void onLogFileParserSucceeded(ParseLogFile parser) { - logEntries = parser.getLogEntries(); - diffEntries = parser.getDiffEntries(); - - rangePane.getDividers().get(0).setPosition(0.0d); - rangePane.getDividers().get(1).setPosition(1.0d); - - rangePane.setDisable(false); - okBtn.setDisable(false); - } - - /** - * Event handler of LogFile button. - * - * @param event ActionEvent of this event. - */ - @FXML - public void onLogFileClick(ActionEvent event) { - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("logResources", new Locale(HeapStatsUtils.getLanguage())); - dialog.setTitle(resource.getString("dialog.filechooser.title")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new ExtensionFilter("Log file (*.csv)", "*.csv"), - new ExtensionFilter("All files", "*.*")); - - List logList = dialog.showOpenMultipleDialog(MainWindowController.getInstance().getOwner()); - - if (logList != null) { - logResourcesController.clearAllItems(); - logDetailsController.clearAllItems(); - - HeapStatsUtils.setDefaultDirectory(logList.get(0).getParent()); - String logListStr = logList.stream() - .map(File::getAbsolutePath) - .collect(Collectors.joining("; ")); - - logFileList.setText(logListStr); - - TaskAdapter task = new TaskAdapter<>(new ParseLogFile(logList, true)); - task.setOnSucceeded(evt -> onLogFileParserSucceeded(task.getTask())); - super.bindTask(task); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - - } - - /** - * Event handler of OK button. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onOkClick(ActionEvent event) { - /* Get range */ - LocalDateTime start = rangeStart.getValue(); - LocalDateTime end = rangeEnd.getValue(); - - List targetLogData = logEntries.parallelStream() - .filter(d -> ((d.getDateTime().compareTo(start) >= 0) && (d.getDateTime().compareTo(end) <= 0))) - .collect(Collectors.toList()); - List targetDiffData = diffEntries.parallelStream() - .filter(d -> ((d.getDateTime().compareTo(start) >= 0) && (d.getDateTime().compareTo(end) <= 0))) - .collect(Collectors.toList()); - - Task task = logResourcesController.createDrawResourceCharts(targetLogData, targetDiffData); - super.bindTask(task); - Thread drawChartThread = new Thread(task); - drawChartThread.start(); - - archiveList.set(FXCollections.observableArrayList(targetLogData.stream() - .filter(d -> d.getArchivePath() != null) - .map(new FunctionWrapper<>(ArchiveData::new)) - .peek(new ConsumerWrapper<>(a -> a.parseArchive())) - .collect(Collectors.toList()))); - } - - /** - * Returns plugin name. This value is used to show in main window tab. - * - * @return Plugin name. - */ - @Override - public String getPluginName() { - return "Log Data"; - } - - @Override - public EventHandler getOnPluginTabSelected() { - return null; - } - - @Override - public String getLicense() { - return PluginController.LICENSE_GPL_V2; - } - - @Override - public Map getLibraryLicense() { - return null; - } - - @Override - public Runnable getOnCloseRequest() { - return null; - } - - @Override - public void setData(Object data, boolean select) { - super.setData(data, select); - logFileList.setText((String) data); - - TaskAdapter task = new TaskAdapter<>(new ParseLogFile(Arrays.asList(new File((String) data)), true)); - task.setOnSucceeded(evt -> onLogFileParserSucceeded(task.getTask())); - super.bindTask(task); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogDataConverter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogDataConverter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2014-2016 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.log; - -import javafx.util.StringConverter; -import jp.co.ntt.oss.heapstats.container.log.LogData; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * StringConverter for LogData. This class provides method to convert - * LocalDateTime in LogData to String. - * - * @author Yasumasa Suenaga. - */ - -public class LogDataConverter extends StringConverter { - - @Override - public String toString(LogData object) { - return object.getDateTime().format(HeapStatsUtils.getDateTimeFormatter()); - } - - /** - * This class DO NOT support this method. - * - * @param string String - * @return UnsupportedOperationException This class cannot convert from string. - */ - @Override - public LogData fromString(String string) { - throw new UnsupportedOperationException("LogData DO NOT convert from String."); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogDetailsController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogDetailsController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2015 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.log.tabs; - -import java.io.IOException; -import java.net.URL; -import java.util.AbstractMap; -import java.util.Map; -import java.util.ResourceBundle; -import javafx.beans.property.ObjectProperty; -import javafx.collections.ObservableList; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.ComboBox; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextArea; -import javafx.scene.control.cell.PropertyValueFactory; -import jp.co.ntt.oss.heapstats.container.log.ArchiveData; -import jp.co.ntt.oss.heapstats.plugin.builtin.log.ArchiveDataConverter; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class for "Log Detail Data" tab in LogData plugin. - */ -public class LogDetailsController implements Initializable { - - @FXML - private TableView> archiveEnvInfoTable; - - @FXML - private TableColumn, String> archiveKeyColumn; - - @FXML - private TableColumn, String> archiveVauleColumn; - - @FXML - private ComboBox fileCombo; - - @FXML - private ComboBox archiveCombo; - - @FXML - private TextArea logArea; - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - archiveCombo.setConverter(new ArchiveDataConverter()); - archiveKeyColumn.setCellValueFactory(new PropertyValueFactory<>("key")); - archiveVauleColumn.setCellValueFactory(new PropertyValueFactory<>("value")); - } - - /** - * Event handler of archive combobox. This handler is fired that user select - * archive. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onArchiveComboAction(ActionEvent event) { - archiveEnvInfoTable.getItems().clear(); - fileCombo.getItems().clear(); - ArchiveData target = archiveCombo.getValue(); - - if (target == null) { - return; - } - - /* - * Convert Map to List. - * Map.Entry of HashMap (HashMap$Node) is package private class. So JavaFX - * cannot access them through reflection API. - * Thus I convert Map.Entry to AbstractMap.SimpleEntry. - */ - target.getEnvInfo().entrySet().forEach(e -> archiveEnvInfoTable.getItems().add(new AbstractMap.SimpleEntry<>(e))); - - fileCombo.getItems().addAll(target.getFileList()); - } - - /** - * Event handler of selecting log in this archive. This handler is fired - * that user select log. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onFileComboAction(ActionEvent event) { - ArchiveData target = archiveCombo.getValue(); - String file = fileCombo.getValue(); - - if((target == null) || (file == null)){ - return; - } - - try { - logArea.setText(target.getFileContents(file)); - } catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - logArea.setText(""); - } - - } - - /** - * Get HeapStats ZIP archive list as Property. - * - * @return List of HeapStats ZIP archive. - */ - public ObjectProperty> archiveListProperty() { - return archiveCombo.itemsProperty(); - } - - /** - * Clear all items in Log Details tab. - */ - public void clearAllItems(){ - fileCombo.getItems().clear(); - logArea.setText(""); - archiveEnvInfoTable.getItems().clear(); -} - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogResourcesController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/tabs/LogResourcesController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,807 +0,0 @@ -/* - * Copyright (C) 2015-2017 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.log.tabs; - -import java.net.URL; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.List; -import java.util.ResourceBundle; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.Node; -import javafx.scene.chart.Axis; -import javafx.scene.chart.LineChart; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.StackedAreaChart; -import javafx.scene.chart.XYChart; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Label; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.Tooltip; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.StackPane; -import javafx.scene.shape.Path; -import javafx.scene.shape.Rectangle; -import jp.co.ntt.oss.heapstats.container.log.ArchiveData; -import jp.co.ntt.oss.heapstats.container.log.DiffData; -import jp.co.ntt.oss.heapstats.container.log.LogData; -import jp.co.ntt.oss.heapstats.container.log.SummaryData; -import jp.co.ntt.oss.heapstats.utils.EpochTimeConverter; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class for "Resource Data" tab in LogData plugin. - */ -public class LogResourcesController implements Initializable { - - @FXML - private GridPane chartGrid; - - @FXML - private StackedAreaChart javaCPUChart; - - private XYChart.Series javaUserUsage; - - private XYChart.Series javaSysUsage; - - @FXML - private StackedAreaChart systemCPUChart; - - private XYChart.Series systemUserUsage; - - private XYChart.Series systemNiceUsage; - - private XYChart.Series systemSysUsage; - - private XYChart.Series systemIdleUsage; - - private XYChart.Series systemIOWaitUsage; - - private XYChart.Series systemIRQUsage; - - private XYChart.Series systemSoftIRQUsage; - - private XYChart.Series systemStealUsage; - - private XYChart.Series systemGuestUsage; - - @FXML - private LineChart javaMemoryChart; - - private XYChart.Series javaVSZUsage; - - private XYChart.Series javaRSSUsage; - - @FXML - private LineChart safepointChart; - - private XYChart.Series safepoints; - - @FXML - private LineChart safepointTimeChart; - - private XYChart.Series safepointTime; - - @FXML - private LineChart threadChart; - - private XYChart.Series threads; - - @FXML - private LineChart monitorChart; - - private XYChart.Series monitors; - - @FXML - private TableView procSummary; - - @FXML - private TableColumn categoryColumn; - - @FXML - private TableColumn valueColumn; - - private ObjectProperty> archiveList; - - private List suspectList; - - private ResourceBundle resource; - - private EpochTimeConverter epochTimeConverter; - - /* Tooltip for Java CPU chart */ - private Tooltip javaCPUTooltip; - - private GridPane javaCPUTooltipGrid; - - private Label javaUserLabel; - - private Label javaSysLabel; - - /* Tooltip for System CPU chart */ - private Tooltip systemCPUTooltip; - - private GridPane systemCPUTooltipGrid; - - private Label systemUserLabel; - - private Label systemNiceLabel; - - private Label systemSysLabel; - - private Label systemIdleLabel; - - private Label systemIOWaitLabel; - - private Label systemIRQLabel; - - private Label systemSoftIRQLabel; - - private Label systemStealLabel; - - private Label systemGuestLabel; - - /* Tooltip for Java Memory chart */ - private Tooltip javaMemoryTooltip; - - private GridPane javaMemoryTooltipGrid; - - private Label javaMemoryVSZLabel; - - private Label javaMemoryRSSLabel; - - /* Generic Tooltip */ - private Tooltip tooltip; - - private void initializeJavaCPUTooltip(){ - javaUserLabel = new Label(); - javaSysLabel = new Label(); - - Rectangle javaUserRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle javaSysRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - - Platform.runLater(() -> { - javaUserRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); - javaSysRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); - }); - - javaCPUTooltipGrid = new GridPane(); - javaCPUTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); - javaCPUTooltipGrid.add(javaUserRect, 0, 0); - javaCPUTooltipGrid.add(new Label("user"), 1, 0); - javaCPUTooltipGrid.add(javaUserLabel, 2, 0); - javaCPUTooltipGrid.add(javaSysRect, 0, 1); - javaCPUTooltipGrid.add(new Label("sys"), 1, 1); - javaCPUTooltipGrid.add(javaSysLabel, 2, 1); - - javaCPUTooltip = new Tooltip(); - javaCPUTooltip.setGraphic(javaCPUTooltipGrid); - javaCPUTooltip.setContentDisplay(ContentDisplay.BOTTOM); - } - - private void initializeJavaMemoryTooltip(){ - javaMemoryVSZLabel = new Label(); - javaMemoryRSSLabel = new Label(); - - Rectangle javaMemoryVSZRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle javaMemoryRSSRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - - Platform.runLater(() -> { - javaMemoryRSSRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); - javaMemoryVSZRect.setStyle("-fx-fill: " + ((Path)javaCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); - }); - - javaMemoryTooltipGrid = new GridPane(); - javaMemoryTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); - javaMemoryTooltipGrid.add(javaMemoryVSZRect, 0, 0); - javaMemoryTooltipGrid.add(new Label("VSZ"), 1, 0); - javaMemoryTooltipGrid.add(javaMemoryVSZLabel, 2, 0); - javaMemoryTooltipGrid.add(javaMemoryRSSRect, 0, 1); - javaMemoryTooltipGrid.add(new Label("RSS"), 1, 1); - javaMemoryTooltipGrid.add(javaMemoryRSSLabel, 2, 1); - - javaMemoryTooltip = new Tooltip(); - javaMemoryTooltip.setGraphic(javaMemoryTooltipGrid); - javaMemoryTooltip.setContentDisplay(ContentDisplay.BOTTOM); - } - - private void initializeSystemCPUTooltip(){ - systemUserLabel = new Label(); - systemNiceLabel = new Label(); - systemSysLabel = new Label(); - systemIdleLabel = new Label(); - systemIOWaitLabel = new Label(); - systemIRQLabel = new Label(); - systemSoftIRQLabel = new Label(); - systemStealLabel = new Label(); - systemGuestLabel = new Label(); - - Rectangle systemUserRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemNiceRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemSysRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemIdleRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemIOWaitRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemIRQRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemSoftIRQRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemStealRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle systemGuestRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - - Platform.runLater(() -> { - systemUserRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series0")).getFill().toString().replace("0x", "#")); - systemNiceRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series1")).getFill().toString().replace("0x", "#")); - systemSysRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series2")).getFill().toString().replace("0x", "#")); - systemIdleRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series3")).getFill().toString().replace("0x", "#")); - systemIOWaitRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series4")).getFill().toString().replace("0x", "#")); - systemIRQRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series5")).getFill().toString().replace("0x", "#")); - systemSoftIRQRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series6")).getFill().toString().replace("0x", "#")); - systemStealRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series7")).getFill().toString().replace("0x", "#")); - systemGuestRect.setStyle("-fx-fill: " + ((Path)systemCPUChart.lookup(".series8")).getFill().toString().replace("0x", "#")); - }); - - systemCPUTooltipGrid = new GridPane(); - systemCPUTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); - systemCPUTooltipGrid.add(systemUserRect, 0, 0); - systemCPUTooltipGrid.add(new Label("user"), 1, 0); - systemCPUTooltipGrid.add(systemUserLabel, 2, 0); - systemCPUTooltipGrid.add(systemNiceRect, 0, 1); - systemCPUTooltipGrid.add(new Label("nice"), 1, 1); - systemCPUTooltipGrid.add(systemNiceLabel, 2, 1); - systemCPUTooltipGrid.add(systemSysRect, 0, 2); - systemCPUTooltipGrid.add(new Label("sys"), 1, 2); - systemCPUTooltipGrid.add(systemSysLabel, 2, 2); - systemCPUTooltipGrid.add(systemIdleRect, 0, 3); - systemCPUTooltipGrid.add(new Label("idle"), 1, 3); - systemCPUTooltipGrid.add(systemIdleLabel, 2, 3); - systemCPUTooltipGrid.add(systemIOWaitRect, 0, 4); - systemCPUTooltipGrid.add(new Label("iowait"), 1, 4); - systemCPUTooltipGrid.add(systemIOWaitLabel, 2, 4); - systemCPUTooltipGrid.add(systemIRQRect, 0, 5); - systemCPUTooltipGrid.add(new Label("IRQ"), 1, 5); - systemCPUTooltipGrid.add(systemIRQLabel, 2, 5); - systemCPUTooltipGrid.add(systemSoftIRQRect, 0, 6); - systemCPUTooltipGrid.add(new Label("Soft IRQ"), 1, 6); - systemCPUTooltipGrid.add(systemSoftIRQLabel, 2, 6); - systemCPUTooltipGrid.add(systemStealRect, 0, 7); - systemCPUTooltipGrid.add(new Label("steal"), 1, 7); - systemCPUTooltipGrid.add(systemStealLabel, 2, 7); - systemCPUTooltipGrid.add(systemGuestRect, 0, 8); - systemCPUTooltipGrid.add(new Label("guest"), 1, 8); - systemCPUTooltipGrid.add(systemGuestLabel, 2, 8); - - systemCPUTooltip = new Tooltip(); - systemCPUTooltip.setGraphic(systemCPUTooltipGrid); - systemCPUTooltip.setContentDisplay(ContentDisplay.BOTTOM); - } - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - resource = rb; - - categoryColumn.setCellValueFactory(new PropertyValueFactory<>("category")); - valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); - - String bgcolor = "-fx-background-color: " + HeapStatsUtils.getChartBgColor() + ";"; - Stream.of(javaCPUChart, systemCPUChart, javaMemoryChart, safepointChart, safepointTimeChart, threadChart, monitorChart) - .peek(c -> c.lookup(".chart").setStyle(bgcolor)) - .forEach(c -> c.getXAxis().setTickMarkVisible(HeapStatsUtils.getTickMarkerSwitch())); - - initializeChartSeries(); - - archiveList = new SimpleObjectProperty<>(); - epochTimeConverter = new EpochTimeConverter(); - - initializeJavaCPUTooltip(); - initializeSystemCPUTooltip(); - initializeJavaMemoryTooltip(); - } - - /** - * Initialize Series in Chart. This method uses to avoid RuntimeException - * which is related to: RT-37994: [FXML] ProxyBuilder does not support - * read-only collections https://javafx-jira.kenai.com/browse/RT-37994 - */ - @SuppressWarnings("unchecked") - private void initializeChartSeries() { - threads = new XYChart.Series<>(); - threads.setName("Threads"); - threadChart.getData().add(threads); - - javaUserUsage = new XYChart.Series<>(); - javaUserUsage.setName("user"); - javaSysUsage = new XYChart.Series<>(); - javaSysUsage.setName("sys"); - javaCPUChart.getData().addAll(javaUserUsage, javaSysUsage); - - systemUserUsage = new XYChart.Series<>(); - systemUserUsage.setName("user"); - systemNiceUsage = new XYChart.Series<>(); - systemNiceUsage.setName("nice"); - systemSysUsage = new XYChart.Series<>(); - systemSysUsage.setName("sys"); - systemIdleUsage = new XYChart.Series<>(); - systemIdleUsage.setName("idle"); - systemIOWaitUsage = new XYChart.Series<>(); - systemIOWaitUsage.setName("I/O wait"); - systemIRQUsage = new XYChart.Series<>(); - systemIRQUsage.setName("IRQ"); - systemSoftIRQUsage = new XYChart.Series<>(); - systemSoftIRQUsage.setName("soft IRQ"); - systemStealUsage = new XYChart.Series<>(); - systemStealUsage.setName("steal"); - systemGuestUsage = new XYChart.Series<>(); - systemGuestUsage.setName("guest"); - systemCPUChart.getData().addAll(systemUserUsage, systemNiceUsage, systemSysUsage, - systemIdleUsage, systemIOWaitUsage, systemIRQUsage, - systemSoftIRQUsage, systemStealUsage, systemGuestUsage); - - javaVSZUsage = new XYChart.Series<>(); - javaVSZUsage.setName("VSZ"); - javaRSSUsage = new XYChart.Series<>(); - javaRSSUsage.setName("RSS"); - javaMemoryChart.getData().addAll(javaVSZUsage, javaRSSUsage); - - safepoints = new XYChart.Series<>(); - safepoints.setName("Safepoints"); - safepointChart.getData().add(safepoints); - - safepointTime = new XYChart.Series<>(); - safepointTime.setName("Safepoint Time"); - safepointTimeChart.getData().add(safepointTime); - - monitors = new XYChart.Series<>(); - monitors.setName("Monitors"); - monitorChart.getData().add(monitors); - } - - private void drawLineInternal(StackPane target, List drawList, String style) { - AnchorPane anchor = null; - XYChart chart = null; - - for (Node node : ((StackPane) target).getChildren()) { - - if (node instanceof AnchorPane) { - anchor = (AnchorPane) node; - } else if (node instanceof XYChart) { - chart = (XYChart) node; - } - - if ((anchor != null) && (chart != null)) { - break; - } - - } - - if ((anchor == null) || (chart == null)) { - throw new IllegalStateException(resource.getString("message.drawline")); - } - - ObservableList anchorChildren = anchor.getChildren(); - anchorChildren.removeAll(anchorChildren.stream() - .filter(n -> n instanceof Rectangle) - .map(n -> (Rectangle) n) - .filter(r -> r.getStyle().equals(style)) - .collect(Collectors.toList())); - - NumberAxis xAxis = (NumberAxis) chart.getXAxis(); - Axis yAxis = chart.getYAxis(); - Label chartTitle = (Label) chart.getChildrenUnmodifiable().stream() - .filter(n -> n.getStyleClass().contains("chart-title")) - .findFirst() - .get(); - - double startX = xAxis.getLayoutX() + 4.0d; - double yPos = yAxis.getLayoutY() + chartTitle.getLayoutY() + chartTitle.getHeight(); - List rectList = drawList.stream() - .map(t -> new Rectangle(xAxis.getDisplayPosition(t) + startX, yPos, 2.0d, yAxis.getHeight())) - .peek(r -> ((Rectangle) r).setStyle(style)) - .collect(Collectors.toList()); - anchorChildren.addAll(rectList); - } - - private void drawArchiveLine() { - - if (archiveList.get().isEmpty()) { - return; - } - - List archiveDateList = archiveList.get() - .stream() - .map(a -> a.getDate().atZone(ZoneId.systemDefault()).toEpochSecond()) - .collect(Collectors.toList()); - chartGrid.getChildren().stream() - .filter(n -> n instanceof StackPane) - .forEach(p -> drawLineInternal((StackPane) p, archiveDateList, "-fx-fill: black;")); - } - - /** - * Draw line which represents to suspect to reboot. This method does not - * clear AnchorPane to draw lines. So this method must be called after - * drawArchiveLine(). - */ - private void drawRebootSuspectLine() { - - if ((suspectList == null) || suspectList.isEmpty()) { - return; - } - - List suspectRebootDateList = suspectList.stream() - .map(d -> d.atZone(ZoneId.systemDefault()).toEpochSecond()) - .collect(Collectors.toList()); - chartGrid.getChildren().stream() - .filter(n -> n instanceof StackPane) - .forEach(p -> drawLineInternal((StackPane) p, suspectRebootDateList, "-fx-fill: yellow;")); - } - - /** - * Draw lines which represents reboot and ZIP archive timing. - */ - public void drawEventLineToChart() { - drawArchiveLine(); - drawRebootSuspectLine(); - } - - /** - * Task class for drawing log chart data. - */ - private class DrawLogChartTask extends Task { - - /* Java CPU */ - private final ObservableList> javaUserUsageBuf; - private final ObservableList> javaSysUsageBuf; - - /* System CPU */ - private final ObservableList> systemUserUsageBuf; - private final ObservableList> systemNiceUsageBuf; - private final ObservableList> systemSysUsageBuf; - private final ObservableList> systemIdleUsageBuf; - private final ObservableList> systemIOWaitUsageBuf; - private final ObservableList> systemIRQUsageBuf; - private final ObservableList> systemSoftIRQUsageBuf; - private final ObservableList> systemStealUsageBuf; - private final ObservableList> systemGuestUsageBuf; - - /* Java Memory */ - private final ObservableList> javaVSZUsageBuf; - private final ObservableList> javaRSSUsageBuf; - - /* Safepoints */ - private final ObservableList> safepointsBuf; - private final ObservableList> safepointTimeBuf; - - /* Threads */ - private final ObservableList> threadsBuf; - - /* Monitor contantion */ - private final ObservableList> monitorsBuf; - - private final List targetLogData; - - private final List targetDiffData; - - private long loopCount; - - private final long totalLoopCount; - - public DrawLogChartTask(List targetLogData, List targetDiffData) { - javaUserUsageBuf = FXCollections.observableArrayList(); - javaSysUsageBuf = FXCollections.observableArrayList(); - systemUserUsageBuf = FXCollections.observableArrayList(); - systemNiceUsageBuf = FXCollections.observableArrayList(); - systemSysUsageBuf = FXCollections.observableArrayList(); - systemIdleUsageBuf = FXCollections.observableArrayList(); - systemIOWaitUsageBuf = FXCollections.observableArrayList(); - systemIRQUsageBuf = FXCollections.observableArrayList(); - systemSoftIRQUsageBuf = FXCollections.observableArrayList(); - systemStealUsageBuf = FXCollections.observableArrayList(); - systemGuestUsageBuf = FXCollections.observableArrayList(); - javaVSZUsageBuf = FXCollections.observableArrayList(); - javaRSSUsageBuf = FXCollections.observableArrayList(); - safepointsBuf = FXCollections.observableArrayList(); - safepointTimeBuf = FXCollections.observableArrayList(); - threadsBuf = FXCollections.observableArrayList(); - monitorsBuf = FXCollections.observableArrayList(); - - this.targetLogData = targetLogData; - this.targetDiffData = targetDiffData; - totalLoopCount = targetDiffData.size() + targetLogData.size(); - } - - private void addDiffData(DiffData data) { - long time = data.getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); - - javaUserUsageBuf.add(new XYChart.Data<>(time, data.getJavaUserUsage())); - javaSysUsageBuf.add(new XYChart.Data<>(time, data.getJavaSysUsage())); - systemUserUsageBuf.add(new XYChart.Data<>(time, data.getCpuUserUsage())); - systemNiceUsageBuf.add(new XYChart.Data<>(time, data.getCpuNiceUsage())); - systemSysUsageBuf.add(new XYChart.Data<>(time, data.getCpuSysUsage())); - systemIdleUsageBuf.add(new XYChart.Data<>(time, data.getCpuIdleUsage())); - systemIOWaitUsageBuf.add(new XYChart.Data<>(time, data.getCpuIOWaitUsage())); - systemIRQUsageBuf.add(new XYChart.Data<>(time, data.getCpuIRQUsage())); - systemSoftIRQUsageBuf.add(new XYChart.Data<>(time, data.getCpuSoftIRQUsage())); - systemStealUsageBuf.add(new XYChart.Data<>(time, data.getCpuStealUsage())); - systemGuestUsageBuf.add(new XYChart.Data<>(time, data.getCpuGuestUsage())); - monitorsBuf.add(new XYChart.Data<>(time, data.getJvmSyncPark())); - safepointsBuf.add(new XYChart.Data<>(time, data.getJvmSafepoints())); - safepointTimeBuf.add(new XYChart.Data<>(time, data.getJvmSafepointTime())); - - updateProgress(); - } - - private void addLogData(LogData data) { - long time = data.getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); - - javaVSZUsageBuf.add(new XYChart.Data<>(time, data.getJavaVSSize() / 1024 / 1024)); - javaRSSUsageBuf.add(new XYChart.Data<>(time, data.getJavaRSSize() / 1024 / 1024)); - threadsBuf.add(new XYChart.Data<>(time, data.getJvmLiveThreads())); - - updateProgress(); - } - - private void setJavaCPUChartTooltip(int idx){ - XYChart.Data userNode = javaUserUsage.getData().get(idx); - XYChart.Data sysNode = javaSysUsage.getData().get(idx); - - EventHandler handler = e -> { - javaCPUTooltip.setText(epochTimeConverter.toString(userNode.getXValue())); - javaUserLabel.setText(String.format("%.02f", userNode.getYValue()) + " %"); - javaSysLabel.setText(String.format("%.02f", sysNode.getYValue()) + " %"); - }; - - Tooltip.install(userNode.getNode(), javaCPUTooltip); - userNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(sysNode.getNode(), javaCPUTooltip); - sysNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - } - - private void setJavaMemoryChartTooltip(int idx){ - XYChart.Data vszNode = javaVSZUsage.getData().get(idx); - XYChart.Data rssNode = javaRSSUsage.getData().get(idx); - - EventHandler handler = e -> { - javaMemoryTooltip.setText(epochTimeConverter.toString(vszNode.getXValue())); - javaMemoryVSZLabel.setText(vszNode.getYValue() + " MB"); - javaMemoryRSSLabel.setText(rssNode.getYValue() + " MB"); - }; - - Tooltip.install(vszNode.getNode(), javaMemoryTooltip); - vszNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(rssNode.getNode(), javaMemoryTooltip); - rssNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - } - - private void setSystemCPUChartTooltip(int idx){ - XYChart.Data userNode = systemUserUsage.getData().get(idx); - XYChart.Data niceNode = systemNiceUsage.getData().get(idx); - XYChart.Data sysNode = systemSysUsage.getData().get(idx); - XYChart.Data idleNode = systemIdleUsage.getData().get(idx); - XYChart.Data iowaitNode = systemIOWaitUsage.getData().get(idx); - XYChart.Data irqNode = systemIRQUsage.getData().get(idx); - XYChart.Data softIrqNode = systemSoftIRQUsage.getData().get(idx); - XYChart.Data stealNode = systemStealUsage.getData().get(idx); - XYChart.Data guestNode = systemGuestUsage.getData().get(idx); - - EventHandler handler = e -> { - systemCPUTooltip.setText(epochTimeConverter.toString(userNode.getXValue())); - systemUserLabel.setText(String.format("%.02f", userNode.getYValue()) + " %"); - systemNiceLabel.setText(String.format("%.02f", niceNode.getYValue()) + " %"); - systemSysLabel.setText(String.format("%.02f", sysNode.getYValue()) + " %"); - systemIdleLabel.setText(String.format("%.02f", idleNode.getYValue()) + " %"); - systemIOWaitLabel.setText(String.format("%.02f", iowaitNode.getYValue()) + " %"); - systemIRQLabel.setText(String.format("%.02f", irqNode.getYValue()) + " %"); - systemSoftIRQLabel.setText(String.format("%.02f", softIrqNode.getYValue()) + " %"); - systemStealLabel.setText(String.format("%.02f", stealNode.getYValue()) + " %"); - systemGuestLabel.setText(String.format("%.02f", guestNode.getYValue()) + " %"); - }; - - Tooltip.install(userNode.getNode(), systemCPUTooltip); - userNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(niceNode.getNode(), systemCPUTooltip); - niceNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(sysNode.getNode(), systemCPUTooltip); - sysNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(idleNode.getNode(), systemCPUTooltip); - idleNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(iowaitNode.getNode(), systemCPUTooltip); - iowaitNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(irqNode.getNode(), systemCPUTooltip); - irqNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(softIrqNode.getNode(), systemCPUTooltip); - softIrqNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(stealNode.getNode(), systemCPUTooltip); - stealNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(guestNode.getNode(), systemCPUTooltip); - guestNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - } - - private void setChartData() { - - if(targetLogData.isEmpty() || targetDiffData.isEmpty()){ - Stream.of(javaMemoryChart, threadChart) - .flatMap(c -> c.getData().stream()) - .forEach(s -> s.getData().clear()); - Stream.of(javaCPUChart, systemCPUChart, safepointChart, safepointTimeChart, monitorChart) - .flatMap(c -> c.getData().stream()) - .forEach(s -> s.getData().clear()); - procSummary.getItems().clear(); - suspectList = null; - return; - } - - /* Set chart range */ - long startLogEpoch = targetLogData.get(0).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); - long endLogEpoch = targetLogData.get(targetLogData.size() - 1).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); - long startDiffEpoch = targetDiffData.get(0).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); - long endDiffEpoch = targetDiffData.get(targetDiffData.size() - 1).getDateTime().atZone(ZoneId.systemDefault()).toEpochSecond(); - Stream.of(javaMemoryChart, threadChart) - .map(c -> (NumberAxis)c.getXAxis()) - .peek(a -> a.setTickUnit((endLogEpoch - startLogEpoch) / HeapStatsUtils.getXTickUnit())) - .peek(a -> a.setLowerBound(startLogEpoch)) - .forEach(a -> a.setUpperBound(endLogEpoch)); - Stream.of(javaCPUChart, systemCPUChart, safepointChart, safepointTimeChart, monitorChart) - .map(c -> (NumberAxis)c.getXAxis()) - .peek(a -> a.setTickUnit((endDiffEpoch - startDiffEpoch) / HeapStatsUtils.getXTickUnit())) - .peek(a -> a.setLowerBound(startDiffEpoch)) - .forEach(a -> a.setUpperBound(endDiffEpoch)); - - /* Replace new chart data */ - javaUserUsage.setData(javaUserUsageBuf); - javaSysUsage.setData(javaSysUsageBuf); - - systemUserUsage.setData(systemUserUsageBuf); - systemNiceUsage.setData(systemNiceUsageBuf); - systemSysUsage.setData(systemSysUsageBuf); - systemIdleUsage.setData(systemIdleUsageBuf); - systemIOWaitUsage.setData(systemIOWaitUsageBuf); - systemIRQUsage.setData(systemIRQUsageBuf); - systemSoftIRQUsage.setData(systemSoftIRQUsageBuf); - systemStealUsage.setData(systemStealUsageBuf); - systemGuestUsage.setData(systemGuestUsageBuf); - - monitors.setData(monitorsBuf); - - safepoints.setData(safepointsBuf); - safepointTime.setData(safepointTimeBuf); - - javaVSZUsage.setData(javaVSZUsageBuf); - javaRSSUsage.setData(javaRSSUsageBuf); - - threads.setData(threadsBuf); - - /* Tooltip setting */ - IntStream.range(0, targetDiffData.size()) - .peek(this::setJavaCPUChartTooltip) - .forEach(this::setSystemCPUChartTooltip); - IntStream.range(0, targetLogData.size()) - .forEach(this::setJavaMemoryChartTooltip); - Tooltip tooltip = new Tooltip(); - Stream.of(threads, safepoints, monitors) - .flatMap(c -> c.getData().stream()) - .peek(d -> Tooltip.install(d.getNode(), tooltip)) - .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText((epochTimeConverter.toString(d.getXValue()) + ": " + d.getYValue())))); - safepointTime.getData() - .stream() - .peek(d -> Tooltip.install(d.getNode(), tooltip)) - .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText(String.format("%s: %d ms", epochTimeConverter.toString(d.getXValue()), d.getYValue())))); - - /* Put summary data to table */ - SummaryData summary = new SummaryData(targetLogData, targetDiffData); - procSummary.setItems(FXCollections.observableArrayList(new SummaryData.SummaryDataEntry(resource.getString("summary.cpu.average"), String.format("%.1f %%", summary.getAverageCPUUsage())), - new SummaryData.SummaryDataEntry(resource.getString("summary.cpu.peak"), String.format("%.1f %%", summary.getMaxCPUUsage())), - new SummaryData.SummaryDataEntry(resource.getString("summary.vsz.average"), String.format("%.1f MB", summary.getAverageVSZ())), - new SummaryData.SummaryDataEntry(resource.getString("summary.vsz.peak"), String.format("%.1f MB", summary.getMaxVSZ())), - new SummaryData.SummaryDataEntry(resource.getString("summary.rss.average"), String.format("%.1f MB", summary.getAverageRSS())), - new SummaryData.SummaryDataEntry(resource.getString("summary.rss.peak"), String.format("%.1f MB", summary.getMaxRSS())), - new SummaryData.SummaryDataEntry(resource.getString("summary.threads.average"), String.format("%.1f", summary.getAverageLiveThreads())), - new SummaryData.SummaryDataEntry(resource.getString("summary.threads.peak"), Long.toString(summary.getMaxLiveThreads())) - )); - - /* - * drawArchiveLine() needs positions in each chart. - * So I call it next event. - */ - suspectList = targetDiffData.stream() - .filter(d -> d.hasMinusData()) - .map(d -> d.getDateTime()) - .collect(Collectors.toList()); - - Platform.runLater(() -> drawEventLineToChart()); - } - - private void updateProgress() { - updateProgress(++loopCount, totalLoopCount); - } - - @Override - protected Void call() throws Exception { - loopCount = 0; - - /* Generate graph data */ - targetDiffData.forEach(this::addDiffData); - targetLogData.forEach(this::addLogData); - - Platform.runLater(this::setChartData); - - return null; - } - - } - - /** - * Get Task instance which draws each chart. - * - * @param targetLogData List of LogData to draw. - * @param targetDiffData List of DiffData to draw. - * - * @return Task instance to draw charts. - */ - public Task createDrawResourceCharts(List targetLogData, List targetDiffData) { - return new DrawLogChartTask(targetLogData, targetDiffData); - } - - /** - * Get HeapStats ZIP archive list as Property. - * - * @return List of HeapStats ZIP archive. - */ - public ObjectProperty> archiveListProperty() { - return archiveList; - } - - /** - * Clear all items in Resource Data tab. - */ - @SuppressWarnings({"raw", "unchecked"}) - public void clearAllItems(){ - - for(Node n : chartGrid.getChildren()){ - if(n instanceof StackPane){ - for(Node cn : ((StackPane)n).getChildren()){ - if(cn instanceof AnchorPane){ // Clear archive and reboot suspect lines. - ((AnchorPane)cn).getChildren().clear(); -} - else if(cn instanceof XYChart){ // Clear chart data. - ((XYChart)cn).getData().stream() - .forEach(s -> ((XYChart.Series)s).getData().clear()); - } - } - } - } - - /* Claer summary table. */ - procSummary.getItems().clear(); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/BindingFilter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/BindingFilter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot; - -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import jp.co.ntt.oss.heapstats.xml.binding.Filter; - -/** - * JAXB binding class for exclude filter. - * This class will be used in JavaFX application. - * - * @author Yasumasa Suenaga - */ -public class BindingFilter extends Filter{ - - private final BooleanProperty hideProp; - - private final BooleanProperty applied; - - public BindingFilter(Filter f){ - setName(f.getName()); - setClasses(f.getClasses()); - hideProp = new SimpleBooleanProperty(f.isHide()); - applied = new SimpleBooleanProperty(false); - } - - public BooleanProperty hideProperty(){ - return hideProp; - } - - public BooleanProperty appliedProperty(){ - return applied; - } - - @Override - public void setHide(boolean visible) { - super.setHide(visible); - hideProp.set(visible); - } - - @Override - public boolean isHide() { - return hideProp.get(); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/ChartColorManager.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/ChartColorManager.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot; - -import java.util.HashMap; -import java.util.Map; - -/** - * Color management class for chart and table cell. - * - * @author Yasumasa Suenaga - */ -public class ChartColorManager { - - private static final String[] chartColorArray; - - private static int colorIndex; - - private static final Map colorCache; - - static { - String colors[] = {"aquamarine", "bisque", "blueviolet", "brown", "blue", "chartreuse", "coral", - "cornflowerblue", "crimson", "darkcyan", "darkgoldenrod", "darkgreen", "darkkhaki", - "darkmagenta", "darkorange", "darksalmon", "darkseagreen", "deeppink", "dodgerblue", - "gold", "green", "orangered", "orchid", "plum", "red", "sandybrown", "slateblue", - "lime", "tomato", "turquoise", "violet", "orange"}; - - chartColorArray = colors; - colorIndex = 0; - colorCache = new HashMap<>(); - } - - /** - * Get next color from sequence. If color which relates className has been - * returned, this method returns same color. - * - * @param className Class name to get. - * @return Color string which relates className. - */ - public static String getNextColor(String className) { - String result = colorCache.computeIfAbsent(className, k -> chartColorArray[colorIndex++]); - - if (colorIndex == chartColorArray.length) { - colorIndex = 0; - } - - return result; - } - - /** - * Get class color from color cache. - * - * @param className Class name to get. - * @return Color string which relates className. transparent if className - * does not exist in cache. - */ - public static String getCurrentColor(String className) { - return colorCache.getOrDefault(className, "transparent"); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,455 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot; - -import java.io.File; -import java.net.URL; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javafx.application.Platform; -import javafx.beans.property.LongProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.ObservableMap; -import javafx.collections.ObservableSet; -import javafx.concurrent.Task; -import javafx.event.ActionEvent; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.Node; -import javafx.scene.chart.Axis; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.XYChart; -import javafx.scene.control.*; -import javafx.scene.layout.AnchorPane; -import javafx.scene.shape.Rectangle; -import javafx.stage.FileChooser; -import javafx.stage.FileChooser.ExtensionFilter; -import jp.co.ntt.oss.heapstats.MainWindowController; -import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; -import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; -import jp.co.ntt.oss.heapstats.container.snapshot.SummaryData; -import jp.co.ntt.oss.heapstats.plugin.PluginController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs.HistogramController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs.RefTreeController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs.SnapshotController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs.SummaryController; -import jp.co.ntt.oss.heapstats.task.CSVDumpGC; -import jp.co.ntt.oss.heapstats.task.CSVDumpHeap; -import jp.co.ntt.oss.heapstats.task.ParseHeader; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; -import jp.co.ntt.oss.heapstats.utils.TaskAdapter; - -/** - * FXML Controller of SnapShot builtin plugin. - * - * @author Yasumasa Suenaga - */ -public class SnapShotController extends PluginController implements Initializable { - - @FXML - private SummaryController summaryController; - - @FXML - private HistogramController histogramController; - - @FXML - private SnapshotController snapshotController; - - @FXML - private RefTreeController reftreeController; - - @FXML - private SplitPane rangePane; - - @FXML - private Label startTimeLabel; - - @FXML - private Label endTimeLabel; - - @FXML - private TextField snapshotList; - - @FXML - private RadioButton radioInstance; - - @FXML - private Button okBtn; - - @FXML - private TabPane snapshotMain; - - @FXML - private Tab histogramTab; - - @FXML - private Tab snapshotTab; - - @FXML - private Tab reftreeTab; - - private ObjectProperty> currentTarget; - - private ObjectProperty summaryData; - - private ObjectProperty> currentClassNameSet; - - private ObjectProperty> snapshotSelectionModel; - - private ObjectProperty>> topNList; - - private List snapShotHeaders; - - private ObjectProperty currentSnapShotHeader; - - private LongProperty currentObjectTag; - - private ObjectProperty rangeStart; - - private ObjectProperty rangeEnd; - - /** - * Update caption of label which represents time of selection. - * - * @param target Label compornent to draw. - * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 . - */ - private void updateRangeLabel(Label target, double newValue){ - if(!Optional.ofNullable(snapShotHeaders).map(List::isEmpty).orElse(true)){ - LocalDateTime start = snapShotHeaders.get(0).getSnapShotDate(); - LocalDateTime end = snapShotHeaders.get(snapShotHeaders.size() - 1).getSnapShotDate(); - long diff = start.until(end, ChronoUnit.MILLIS); - LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS); - - if(target == startTimeLabel){ - rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS)); - } - else{ - rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS)); - } - - target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter())); - } - } - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - super.initialize(url, rb); - - summaryData = new SimpleObjectProperty<>(); - summaryController.summaryDataProperty().bind(summaryData); - currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); - summaryController.currentTargetProperty().bind(currentTarget); - histogramController.currentTargetProperty().bind(currentTarget); - snapshotController.currentTargetProperty().bind(currentTarget); - currentClassNameSet = new SimpleObjectProperty<>(); - summaryController.currentClassNameSetProperty().bind(currentClassNameSet); - histogramController.currentClassNameSetProperty().bind(currentClassNameSet); - histogramController.instanceGraphProperty().bind(radioInstance.selectedProperty()); - snapshotController.instanceGraphProperty().bind(radioInstance.selectedProperty()); - snapshotSelectionModel = new SimpleObjectProperty<>(); - snapshotSelectionModel.bind(snapshotController.snapshotSelectionModelProperty()); - histogramController.snapshotSelectionModelProperty().bind(snapshotSelectionModel); - histogramController.setDrawRebootSuspectLine(this::drawRebootSuspectLine); - topNList = new SimpleObjectProperty<>(); - topNList.bind(histogramController.topNListProperty()); - snapshotController.topNListProperty().bind(topNList); - currentSnapShotHeader = new SimpleObjectProperty<>(); - currentSnapShotHeader.bind(snapshotController.snapshotSelectionModelProperty().get().selectedItemProperty()); - reftreeController.currentSnapShotHeaderProperty().bind(currentSnapShotHeader); - - currentObjectTag = new SimpleLongProperty(); - // TODO: - // Why can I NOT use binding? - // First binding is enabled. But second binding is disabled. - // So I use ChangeListener to avoid this issue. - //currentObjectTag.bind(histogramController.currentObjectTagProperty()); - //currentObjectTag.bind(snapshotController.currentObjectTagProperty()); - histogramController.currentObjectTagProperty().addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(m -> currentObjectTag.set((Long) m))); - snapshotController.currentObjectTagProperty().addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(m -> currentObjectTag.set((Long) m))); - reftreeController.currentObjectTagProperty().bind(currentObjectTag); - - snapshotMain.getSelectionModel().selectedItemProperty().addListener(this::onTabChanged); - - snapShotHeaders = null; - rangeStart = new SimpleObjectProperty<>(); - rangeEnd = new SimpleObjectProperty<>(); - - rangePane.getDividers().get(0).positionProperty().addListener((v, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue())); - rangePane.getDividers().get(1).positionProperty().addListener((v, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue())); - - setOnWindowResize((v, o, n) -> Platform.runLater(() -> Stream.of(summaryController.getHeapChart(), - summaryController.getInstanceChart(), - summaryController.getGcTimeChart(), - summaryController.getMetaspaceChart(), - histogramController.getTopNChart()) - .forEach(c -> Platform.runLater(() -> drawRebootSuspectLine(c))))); - - histogramController.setTaskExecutor(t -> { - bindTask(t); - (new Thread(t)).start(); - }); - } - - private void onTabChanged(ObservableValue observable, Tab oldValue, Tab newValue) { - if (newValue == reftreeTab) { - if (oldValue == histogramTab) { - currentObjectTag.set(histogramController.currentObjectTagProperty().get()); - } else if (oldValue == snapshotTab) { - currentObjectTag.set(snapshotController.currentObjectTagProperty().get()); - } - } - } - - /** - * onSucceeded event handler for ParseHeader. - * - * @param headers New SnapShotHeader list. - */ - private void onSnapShotParserSucceeded(List headers) { - snapShotHeaders = headers; - - rangePane.getDividers().get(0).setPosition(0.0d); - rangePane.getDividers().get(1).setPosition(1.0d); - - rangePane.setDisable(false); - okBtn.setDisable(false); - } - - /** - * Event handler of SnapShot file button. - * - * @param event ActionEvent of this event. - */ - @FXML - public void onSnapshotFileClick(ActionEvent event) { - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); - - dialog.setTitle(resource.getString("dialog.filechooser.title")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new ExtensionFilter("SnapShot file (*.dat)", "*.dat"), - new ExtensionFilter("All files", "*.*")); - - List snapshotFileList = dialog.showOpenMultipleDialog(MainWindowController.getInstance().getOwner()); - - if (snapshotFileList != null) { - clearAllItems(); - - HeapStatsUtils.setDefaultDirectory(snapshotFileList.get(0).getParent()); - List files = snapshotFileList.stream() - .map(File::getAbsolutePath) - .collect(Collectors.toList()); - snapshotList.setText(files.stream().collect(Collectors.joining("; "))); - - TaskAdapter task = new TaskAdapter<>(new ParseHeader(files, HeapStatsUtils.getReplaceClassName(), true)); - task.setOnSucceeded(evt -> onSnapShotParserSucceeded(task.getTask().getSnapShotList())); - super.bindTask(task); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - - } - - /** - * Event handler of OK button. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onOkClick(ActionEvent event) { - /* Get range */ - LocalDateTime start = rangeStart.getValue(); - LocalDateTime end = rangeEnd.getValue(); - currentTarget.set(FXCollections.observableArrayList(snapShotHeaders.stream() - .filter(d -> ((d.getSnapShotDate().compareTo(start) >= 0) && (d.getSnapShotDate().compareTo(end) <= 0))) - .collect(Collectors.toList()))); - currentClassNameSet.set(FXCollections.observableSet()); - summaryData.set(new SummaryData(currentTarget.get())); - - Task topNTask = histogramController.getDrawTopNDataTask(currentTarget.get(), true, null); - super.bindTask(topNTask); - Thread topNThread = new Thread(topNTask); - topNThread.start(); - - Task summarizeTask = summaryController.getCalculateGCSummaryTask(this::drawRebootSuspectLine); - super.bindTask(summarizeTask); - Thread summarizeThread = new Thread(summarizeTask); - summarizeThread.start(); - } - - /** - * Returns plugin name. This value is used to show in main window tab. - * - * @return Plugin name. - */ - @Override - public String getPluginName() { - return "SnapShot Data"; - } - - @Override - public EventHandler getOnPluginTabSelected() { - return null; - } - - @Override - public String getLicense() { - return PluginController.LICENSE_GPL_V2; - } - - @Override - public Map getLibraryLicense() { - Map licenseMap = new HashMap<>(); - licenseMap.put("JGraphX", PluginController.LICENSE_BSD); - - return licenseMap; - } - - /** - * Dump GC Statistics to CSV. - * - * @param isSelected If this value is true, this method dumps data which is - * selected time range, otherwise this method dumps all snapshot data. - */ - public void dumpGCStatisticsToCSV(boolean isSelected) { - FileChooser dialog = new FileChooser(); - dialog.setTitle("Select CSV files"); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new ExtensionFilter("CSV file (*.csv)", "*.csv"), - new ExtensionFilter("All files", "*.*")); - File csvFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); - - if (csvFile != null) { - TaskAdapter task = new TaskAdapter<>(new CSVDumpGC(csvFile, isSelected ? currentTarget.get() : snapShotHeaders)); - super.bindTask(task); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - - } - - /** - * Dump Java Class Histogram to CSV. - * - * @param isSelected If this value is true, this method dumps data which is - * selected in class filter, otherwise this method dumps all snapshot data. - */ - public void dumpClassHistogramToCSV(boolean isSelected) { - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); - - dialog.setTitle(resource.getString("dialog.csvchooser.title")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new ExtensionFilter("CSV file (*.csv)", "*.csv"), - new ExtensionFilter("All files", "*.*")); - File csvFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); - - if (csvFile != null) { - Predicate filter = histogramController.getFilter(); - TaskAdapter task = new TaskAdapter<>(new CSVDumpHeap(csvFile, isSelected ? currentTarget.get() : snapShotHeaders, isSelected ? filter : null, HeapStatsUtils.getReplaceClassName())); - super.bindTask(task); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - - } - - @Override - public Runnable getOnCloseRequest() { - return null; - } - - @Override - public void setData(Object data, boolean select) { - super.setData(data, select); - snapshotList.setText((String) data); - - TaskAdapter task = new TaskAdapter<>(new ParseHeader(Arrays.asList((String) data), HeapStatsUtils.getReplaceClassName(), true)); - task.setOnSucceeded(evt -> onSnapShotParserSucceeded(task.getTask().getSnapShotList())); - super.bindTask(task); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - - private void drawRebootSuspectLine(XYChart target) { - - if (target.getData().isEmpty() || target.getData().get(0).getData().isEmpty()) { - return; - } - - AnchorPane anchor = (AnchorPane) target.getParent().getChildrenUnmodifiable() - .stream() - .filter(n -> n instanceof AnchorPane) - .findAny() - .get(); - ObservableList anchorChildren = anchor.getChildren(); - anchorChildren.clear(); - - NumberAxis xAxis = (NumberAxis)target.getXAxis(); - Axis yAxis = target.getYAxis(); - Label chartTitle = (Label) target.getChildrenUnmodifiable().stream() - .filter(n -> n.getStyleClass().contains("chart-title")) - .findFirst() - .get(); - - double startX = xAxis.getLayoutX() + 4.0d; - double yPos = yAxis.getLayoutY() + chartTitle.getLayoutY() + chartTitle.getHeight(); - List rectList = summaryData.get().getRebootSuspectList() - .stream() - .map(d -> d.atZone(ZoneId.systemDefault()).toEpochSecond()) - .map(s -> new Rectangle(xAxis.getDisplayPosition(s) + startX, yPos, 4d, yAxis.getHeight())) - .peek(r -> ((Rectangle) r).setStyle("-fx-fill: yellow;")) - .collect(Collectors.toList()); - anchorChildren.addAll(rectList); - } - - /** - * Clear all items in SnapShot plugin. - */ - public void clearAllItems(){ - currentTarget.set(FXCollections.emptyObservableList()); - summaryData.set(null); - currentClassNameSet.set(FXCollections.emptyObservableSet()); - currentObjectTag.set(-1); - - summaryController.clearAllItems(); - histogramController.clearAllItems(); - snapshotController.clearAllItems(); -} - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotHeaderConverter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotHeaderConverter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2014-2016 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot; - -import java.util.Optional; -import javafx.util.StringConverter; -import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * StringConverter for LocalDateTime of SnapShotHeader. - * This class is used at JavaFX controls. - * - * @author Yasumasa Suenaga - */ -public class SnapShotHeaderConverter extends StringConverter { - - @Override - public String toString(SnapShotHeader object) { - return Optional.ofNullable(object) - .map(o -> o.getSnapShotDate().format(HeapStatsUtils.getDateTimeFormatter())) - .orElse(null); - } - - @Override - public SnapShotHeader fromString(String string) { - throw new UnsupportedOperationException("SnapShotHeader DO NOT convert from String."); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/HistogramController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/HistogramController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,579 +0,0 @@ -/* - * Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs; - -import java.io.File; -import java.net.URL; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.LongProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.ObservableMap; -import javafx.collections.ObservableSet; -import javafx.concurrent.Task; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.Node; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.StackedAreaChart; -import javafx.scene.chart.XYChart; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.control.ListView; -import javafx.scene.control.SelectionMode; -import javafx.scene.control.SelectionModel; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.control.cell.CheckBoxTableCell; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.AnchorPane; -import javafx.stage.FileChooser; -import javax.xml.bind.JAXB; -import jp.co.ntt.oss.heapstats.container.snapshot.DiffData; -import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; -import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.BindingFilter; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.ChartColorManager; -import jp.co.ntt.oss.heapstats.task.DiffCalculator; -import jp.co.ntt.oss.heapstats.utils.EpochTimeConverter; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; -import jp.co.ntt.oss.heapstats.utils.TaskAdapter; -import jp.co.ntt.oss.heapstats.xml.binding.Filter; -import jp.co.ntt.oss.heapstats.xml.binding.Filters; - -/** - * FXML Controller class for "Histogram" tab in SnapShot plugin. - */ -public class HistogramController implements Initializable { - - @FXML - private TableView excludeTable; - - @FXML - private TableColumn hideColumn; - - @FXML - private TableColumn excludeNameColumn; - - @FXML - private TextField searchText; - - @FXML - private ListView searchList; - - @FXML - private Button selectFilterApplyBtn; - - @FXML - private StackedAreaChart topNChart; - - @FXML - private AnchorPane topNChartAnchor; - - @FXML - private NumberAxis topNYAxis; - - @FXML - private TableView lastDiffTable; - - @FXML - private TableColumn colorColumn; - - @FXML - private TableColumn classNameColumn; - - @FXML - private TableColumn classLoaderColumn; - - @FXML - private TableColumn instanceColumn; - - @FXML - private TableColumn totalSizeColumn; - - private ObjectProperty> currentTarget; - - private ObjectProperty> currentClassNameSet; - - private BooleanProperty instanceGraph; - - private ObjectProperty> snapshotSelectionModel; - - private boolean searchFilterEnable; - - private boolean excludeFilterEnable; - - private ObjectProperty>> topNList; - - private LongProperty currentObjectTag; - - private Consumer> drawRebootSuspectLine; - - private Consumer> taskExecutor; - - private List hideRegexList; - - private EpochTimeConverter epochTimeConverter; - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - instanceGraph = new SimpleBooleanProperty(); - currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); - currentClassNameSet = new SimpleObjectProperty<>(FXCollections.emptyObservableSet()); - snapshotSelectionModel = new SimpleObjectProperty<>(); - topNList = new SimpleObjectProperty<>(); - currentObjectTag = new SimpleLongProperty(); - searchFilterEnable = false; - excludeFilterEnable = false; - - hideColumn.setCellValueFactory(new PropertyValueFactory<>("hide")); - hideColumn.setCellFactory(CheckBoxTableCell.forTableColumn(hideColumn)); - - excludeNameColumn.setCellFactory(p -> new TableCell(){ - @Override - protected void updateItem(String item, boolean empty) { - super.updateItem(item, empty); - BindingFilter filter = (BindingFilter)getTableRow().getItem(); - - if(!empty && (filter != null)){ - styleProperty().bind(Bindings.createStringBinding(() -> filter.appliedProperty().get() ? "-fx-background-color: skyblue;" : "-fx-background-color: white;", filter.appliedProperty())); - setText(filter.getName()); - } - - } - }); - - colorColumn.setCellFactory(p -> new TableCell() { - @Override - protected void updateItem(String item, boolean empty) { - super.updateItem(item, empty); - String style = Optional.ofNullable((DiffData) getTableRow().getItem()) - .filter(d -> d.isRanked()) - .map(d -> "-fx-background-color: " + ChartColorManager.getNextColor(d.getClassName())) - .orElse("-fx-background-color: transparent;"); - setStyle(style); - } - }); - - classNameColumn.setCellValueFactory(new PropertyValueFactory<>("className")); - classLoaderColumn.setCellValueFactory(new PropertyValueFactory<>("classLoaderName")); - instanceColumn.setCellValueFactory(new PropertyValueFactory<>("instances")); - instanceColumn.setSortType(TableColumn.SortType.DESCENDING); - totalSizeColumn.setCellValueFactory(new PropertyValueFactory<>("totalSize")); - totalSizeColumn.setSortType(TableColumn.SortType.DESCENDING); - - searchList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); - - topNChart.lookup(".chart").setStyle("-fx-background-color: " + HeapStatsUtils.getChartBgColor() + ";"); - topNChart.getXAxis().setTickMarkVisible(HeapStatsUtils.getTickMarkerSwitch()); - - searchFilterEnable = false; - excludeFilterEnable = false; - - selectFilterApplyBtn.disableProperty().bind(searchList.selectionModelProperty().getValue().selectedItemProperty().isNull()); - currentObjectTag.bind(Bindings.createLongBinding(() -> Optional.ofNullable(lastDiffTable.getSelectionModel().getSelectedItem()) - .map(d -> d.getTag()) - .orElse(0xffffffffffffffffl), - lastDiffTable.getSelectionModel().selectedItemProperty())); - - epochTimeConverter = new EpochTimeConverter(); - } - - /** - * Build TopN Data for Chart with givien data. - * - * @param header SnapShot header which you want to build. - * @param seriesMap Chart series map which is contains class name as key, - * chart series as value. - * @param objData ObjectData which is you want to build. - */ - private void buildTopNChartData(SnapShotHeader header, Map> seriesMap, ObjectData objData) { - XYChart.Series series = seriesMap.get(objData.getName()); - - if (series == null) { - series = new XYChart.Series<>(); - series.setName(objData.getName()); - topNChart.getData().add(series); - seriesMap.put(objData.getName(), series); - } - - long time = header.getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); - long yValue = instanceGraph.get() ? objData.getCount() - : objData.getTotalSize() / 1024 / 1024; - XYChart.Data data = new XYChart.Data<>(time, yValue); - - series.getData().add(data); - String unit = instanceGraph.get() ? "instances" : "MB"; - String tip = String.format("%s: %s, %d " + unit, series.getName(), epochTimeConverter.toString(time), yValue); - Tooltip.install(data.getNode(), new Tooltip(tip)); - } - - private void setTopNChartColor(XYChart.Series series) { - String color = ChartColorManager.getNextColor(series.getName()); - - series.getNode().lookup(".chart-series-area-line").setStyle(String.format("-fx-stroke: %s;", color)); - series.getNode().lookup(".chart-series-area-fill").setStyle(String.format("-fx-fill: %s;", color)); - - series.getData().stream() - .map(d -> d.getNode().lookup(".chart-area-symbol")) - .forEach(n -> n.setStyle(String.format("-fx-background-color: %s, white;", color))); - } - - /** - * onSucceeded event handler for DiffTask. - * - * @param diff Target task. - * @param seriesMap Chart series map which is contains class name as key, - * chart series as value. - */ - private void onDiffTaskSucceeded(DiffCalculator diff, Map> seriesMap) { - /* Set chart range */ - long startEpoch = currentTarget.get().get(0).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); - long endEpoch = currentTarget.get().get(currentTarget.get().size() - 1).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); - NumberAxis xAxis = (NumberAxis)topNChart.getXAxis(); - xAxis.setTickUnit((endEpoch - startEpoch) / HeapStatsUtils.getXTickUnit()); - xAxis.setLowerBound(startEpoch); - xAxis.setUpperBound(endEpoch); - - topNList.set(FXCollections.observableMap(diff.getTopNList())); - - currentTarget.get().stream() - .forEachOrdered(h -> topNList.get().get(h.getSnapShotDate()).stream() - .forEachOrdered(o -> buildTopNChartData(h, seriesMap, o))); - - lastDiffTable.getItems().addAll(diff.getLastDiffList()); - lastDiffTable.getSortOrder().add(instanceGraph.get() ? instanceColumn : totalSizeColumn); - topNChart.getData().forEach(this::setTopNChartColor); - Platform.runLater(() -> drawRebootSuspectLine.accept(topNChart)); - - long maxVal = topNChart.getData().stream() - .flatMap(s -> s.dataProperty().get().stream()) - .collect(Collectors.groupingBy(d -> d.getXValue(), Collectors.summingLong(d -> d.getYValue()))) - .values() - .stream() - .mapToLong(Long::longValue) - .max() - .getAsLong(); - - topNYAxis.setUpperBound(maxVal * 1.05d); - topNYAxis.setTickUnit(maxVal / 20); - topNYAxis.setLabel(instanceGraph.get() ? "instances" : "MB"); - - snapshotSelectionModel.get().selectLast(); - - if(excludeFilterEnable){ - excludeTable.getItems().stream() - .forEach(f -> f.appliedProperty().set(f.isHide())); - } - else{ - excludeTable.getItems().stream() - .forEach(f -> f.appliedProperty().set(false)); - } - - } - - private void putIfAbsentToExcludeFilter(Filter filter){ - Optional exists = excludeTable.getItems().stream() - .filter(f -> f.getName().equals(filter.getName())) - .findAny(); - - if(exists.isPresent()){ - Alert dialog = new Alert(Alert.AlertType.ERROR, filter.getName() + " already exists!", ButtonType.OK); - dialog.showAndWait(); - } - else{ - excludeTable.getItems().add(new BindingFilter(filter)); - } - - } - - /** - * Event handler for adding exclude filter. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onAddClick(ActionEvent event) { - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); - - dialog.setTitle(resource.getString("dialog.filterchooser.title")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Filter file (*.xml)", "*.xml"), - new FileChooser.ExtensionFilter("All files", "*.*")); - - List excludeFilterList = dialog.showOpenMultipleDialog(((Node) event.getSource()).getScene().getWindow()); - if (excludeFilterList != null) { - excludeFilterList.stream() - .map(f -> (Filters) JAXB.unmarshal(f, Filters.class)) - .filter(f -> f != null) - .flatMap(f -> f.getFilter().stream()) - .forEach(this::putIfAbsentToExcludeFilter); - } - - } - - /** - * Drawing and Showing table with beging selected value. - * - * @return Class name filter. null if any filter is disabled. - */ - public Predicate getFilter() { - HashSet targetSet = new HashSet<>(searchList.getSelectionModel().getSelectedItems()); - Predicate searchFilter = o -> targetSet.contains(o.getName()); - Predicate hideFilter = o -> hideRegexList.stream().noneMatch(s -> o.getName().matches(s)); - - Predicate filter; - - if (searchFilterEnable && excludeFilterEnable) { - filter = searchFilter.and(hideFilter); - } else if (searchFilterEnable) { - filter = searchFilter; - } else if (excludeFilterEnable) { - filter = hideFilter; - } else { - filter = null; - } - - return filter; - } - - /** - * Event handler of apply button in exclude filter. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onHiddenFilterApply(ActionEvent event) { - excludeFilterEnable = true; - hideRegexList = excludeTable.getItems().stream() - .filter(f -> f.isHide()) - .flatMap(f -> f.getClasses().getName().stream()) - .map(s -> ".*" + s + ".*") - .collect(Collectors.toList()); - Predicate filter = getFilter(); - - taskExecutor.accept(getDrawTopNDataTask(currentTarget.get(), false, filter)); - } - - /** - * Event handler of changing search TextField. - * - * @param event KeyEvent of this event. - */ - @FXML - private void onSearchTextChanged(KeyEvent event) { - Stream searchStrings = currentClassNameSet.get().stream(); - - if(excludeFilterEnable){ - searchStrings = searchStrings.filter(n -> hideRegexList.stream().noneMatch(s -> n.matches(s))); - } - - searchList.getItems().clear(); - searchList.getItems().addAll(searchStrings.filter(n -> n.contains(searchText.getText())) - .collect(Collectors.toList())); - } - - /** - * Selection method for incremental search. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onSelectFilterApply(ActionEvent event) { - searchFilterEnable = true; - Predicate filter = getFilter(); - - taskExecutor.accept(getDrawTopNDataTask(currentTarget.get(), false, filter)); - } - - /** - * Event handler of clear button. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onSelectFilterClear(ActionEvent event) { - searchFilterEnable = false; - excludeFilterEnable = false; - - searchText.setText(""); - searchList.getItems().clear(); - - taskExecutor.accept(getDrawTopNDataTask(currentTarget.get(), true, null)); - } - - /** - * Get task for drawing Top N data to Chart and Table. - * - * @param target SnapShot to be drawed. - * @param includeOthers *Others* data should be included in this Top N data. - * @param predicate Filter function. - * @return Task for drawing Top N data. - */ - public Task getDrawTopNDataTask(List target, boolean includeOthers, Predicate predicate) { - topNChart.getData().clear(); - lastDiffTable.getItems().clear(); - Map> seriesMap = new HashMap<>(); - - TaskAdapter diff = new TaskAdapter<>(new DiffCalculator(target, HeapStatsUtils.getRankLevel(), - includeOthers, predicate, HeapStatsUtils.getReplaceClassName(), instanceGraph.get())); - diff.setOnSucceeded(evt -> onDiffTaskSucceeded(diff.getTask(), seriesMap)); - - return diff; - } - - /** - * Set Consumer for drawing reboot line. - * - * @param drawRebootSuspectLine Consumer for drawing reboot line. - */ - public void setDrawRebootSuspectLine(Consumer> drawRebootSuspectLine) { - this.drawRebootSuspectLine = drawRebootSuspectLine; - } - - /** - * Set Consumer for executing JavaFX task. This Consumer is used for tasks - * which should be shown ProgressIndicator. - * - * @param taskExecutor Consumer for executing JavaFX task. - */ - public void setTaskExecutor(Consumer> taskExecutor) { - this.taskExecutor = taskExecutor; - } - - /** - * Get property which contains chart category (instance count or object - * size). - * - * @return Property which contains chart category. - */ - public BooleanProperty instanceGraphProperty() { - return instanceGraph; - } - - /** - * Get property of list of SnapShotHeader. - * - * @return Property of list of SnapShotHeader. - */ - public ObjectProperty> currentTargetProperty() { - return currentTarget; - } - - /** - * Get property of class name set. - * - * @return Property of class name set. - */ - public ObjectProperty> currentClassNameSetProperty() { - return currentClassNameSet; - } - - /** - * Get property for current SnapShot selection. - * - * @return Property for current SnapShot selection. - */ - public ObjectProperty> snapshotSelectionModelProperty() { - return snapshotSelectionModel; - } - - /** - * Get TopN chart. - * - * @return TopN chart. - */ - public StackedAreaChart getTopNChart() { - return topNChart; - } - - /** - * Get property of Map of Top N list. - * - * @return Property of map of Top N list. - */ - public ObjectProperty>> topNListProperty() { - return topNList; - } - - /** - * Get property of current Object tag. - * - * @return Property of currentObjectTag. - */ - public LongProperty currentObjectTagProperty() { - return currentObjectTag; - } - - /** - * Get selected diff data. - * - * @return Selected diff data. - */ - public DiffData getSelectedData() { - return lastDiffTable.getSelectionModel().getSelectedItem(); - } - - /** - * Clear all items in Histogram tab. - */ - public void clearAllItems(){ - excludeTable.getItems().stream() - .forEach((f -> f.setHide(false))); - excludeFilterEnable = false; - - searchText.setText(""); - searchList.getItems().clear(); - searchFilterEnable = false; - - topNChart.getData().clear(); - topNChartAnchor.getChildren().clear(); - - lastDiffTable.getItems().clear(); -} - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/RefTreeController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/RefTreeController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2014-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs; - -import javafx.scene.layout.VBox; -import jp.co.ntt.oss.heapstats.snapshot.ReferenceTracker; -import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; -import com.mxgraph.model.mxCell; -import com.mxgraph.model.mxGeometry; -import com.mxgraph.model.mxICell; -import com.mxgraph.swing.mxGraphComponent; -import com.mxgraph.swing.util.mxGraphTransferable; -import java.awt.Point; -import java.awt.datatransfer.DataFlavor; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.OptionalInt; -import java.util.ResourceBundle; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.beans.property.LongProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.embed.swing.SwingNode; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.ButtonType; -import javafx.scene.control.CheckBox; -import javafx.scene.control.Label; -import javafx.scene.control.RadioButton; -import javafx.scene.layout.AnchorPane; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; -import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotHeaderConverter; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class of Reference Data tab. - * - * @author Yasumasa Suenaga - */ -public class RefTreeController implements Initializable, MouseListener { - - /** - * Reference to the region for displaying the object. - */ - private ReferenceGraph graph; - - /** - * Reference to the enclosing graph component. - */ - private mxGraphComponent graphComponent; - - @FXML - private VBox topVBox; - - @FXML - private Label snapshotLabel; - - @FXML - private RadioButton radioParent; - - @FXML - private RadioButton radioSize; - - @FXML - private CheckBox rankCheckBox; - - @FXML - private SwingNode graphNode; - - private ObjectProperty currentSnapShotHeader; - - private LongProperty currentObjectTag; - - private ResourceBundle resource; - - private void buildTab() { - if ((currentSnapShotHeader.get() != null) - && currentSnapShotHeader.get().hasReferenceData() - && (currentObjectTag.get() != -1)){ - SwingUtilities.invokeLater(() -> initializeSwingNode()); - } - else{ - /* - * Initialize SwingNode with dummy mxGraphComponent. - * SwingNode seems to hook focus ungrab event. - * If SwingNode is empty, IllegalArgumentException with "null source" is - * thrown when another stage (e.g. about dialog) is shown. - * To avoid this, dummy mxGraphComponent is set to SwingNode at first. - */ - SwingUtilities.invokeLater(() -> graphNode.setContent(new mxGraphComponent(new ReferenceGraph()))); - } - } - - /** - * Initialize method for SwingNode. This method is called by Swing Event - * Dispatcher Thread. - */ - private void initializeSwingNode() { - graph = new ReferenceGraph(); - graph.setCellsEditable(false); - - graph.getModel().beginUpdate(); - { - ObjectData data = currentSnapShotHeader.get().getSnapShot(HeapStatsUtils.getReplaceClassName()).get(currentObjectTag.get()); - if (data == null) { - throw new IllegalStateException(resource.getString("reftree.message.notselect")); - } - ReferenceCell cell = new ReferenceCell(data, true, false); - graph.addCell(cell); - } - graph.getModel().endUpdate(); - - graph.setEdgeStyle(); - - graphComponent = new mxGraphComponent(graph); - graphComponent.getGraphControl().addMouseListener(this); - graphComponent.setToolTips(true); - - graphNode.setContent(graphComponent); - - /* - * FIXME! - * SwingNode is not shown immediately. - * So I call repaint() method at last and request layout() call. - */ - graphComponent.repaint(); - Platform.runLater(() -> topVBox.layout()); - } - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - resource = rb; - ReferenceCell.initialize(); - currentSnapShotHeader = new SimpleObjectProperty<>(); - currentObjectTag = new SimpleLongProperty(); - currentObjectTag.addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(t -> buildTab())); - snapshotLabel.textProperty().bind(Bindings.createStringBinding(() -> (new SnapShotHeaderConverter()).toString(currentSnapShotHeader.get()), currentSnapShotHeader)); - - /* - * Initialize SwingNode with dummy mxGraphComponent. - * SwingNode seems to hook focus ungrab event. - * If SwingNode is empty, IllegalArgumentException with "null source" is - * thrown when another stage (e.g. about dialog) is shown. - * To avoid this, dummy mxGraphComponent is set to SwingNode at first. - */ - SwingUtilities.invokeLater(() -> { - mxGraphComponent dummyGraphComponent = new mxGraphComponent(new ReferenceGraph()); - graphNode.setContent(dummyGraphComponent); - }); - - } - - /** - * Add reference cells to graph. This method creates cell which represents - * child, and connects to it from parent. - * - * @param parentCell Parent cell - * @param child Child data. - */ - private void addReferenceCell(ReferenceCell parentCell, ObjectData objData) { - ReferenceCell cell = null; - mxCell tmp = (mxCell) parentCell.getParent(); - - for (int i = 0; i < tmp.getChildCount(); i++) { - mxICell target = tmp.getChildAt(i); - - if (target.isVertex() && (((ReferenceCell) target).getTag() == objData.getTag())) { - cell = (ReferenceCell) target; - } - - } - - ReferenceCell edge = new ReferenceCell(objData, false, true); - - if (cell == null) { - cell = new ReferenceCell(objData, false, false); - graph.addCell(cell); - } - - long size = objData.getTotalSize() / 1024; // KB - - edge.setValue((size == 0) ? objData.getCount() + "\n< 1" - : objData.getCount() + "\n" + objData.getTotalSize() / 1024); // KB - - graph.addEdge(edge, graph.getDefaultParent(), parentCell, cell, 1); - } - - /** - * Displayed on the graph to get a child or of a cell object you clicked, - * the information of the parent. - * - * @param parentCell Cell object you clicked - */ - private void drawMind(ReferenceCell parentCell) { - - if ((parentCell.getEdgeCount() > 1) - || (parentCell.isRoot() && (parentCell.getEdgeCount() > 0)) - || (parentCell.isEdge())) { - return; - } - - OptionalInt rankLevel = rankCheckBox.isSelected() ? OptionalInt.of(HeapStatsUtils.getRankLevel()) - : OptionalInt.empty(); - ReferenceTracker refTracker = new ReferenceTracker(currentSnapShotHeader.get().getSnapShot(HeapStatsUtils.getReplaceClassName()), rankLevel, Optional.empty()); - List objectList = radioParent.isSelected() ? refTracker.getParents(parentCell.getTag(), radioSize.isSelected()) - : refTracker.getChildren(parentCell.getTag(), radioSize.isSelected()); - - if (objectList.isEmpty()) { - return; - } - - graph.getModel().beginUpdate(); - { - objectList.forEach(o -> addReferenceCell(parentCell, o)); - - if (mxGraphTransferable.dataFlavor == null) { - try { - mxGraphTransferable.dataFlavor = new DataFlavor( - DataFlavor.javaJVMLocalObjectMimeType + "; class=com.mxgraph.swing.util.mxGraphTransferable", - null, new mxGraphTransferable(null, null).getClass().getClassLoader()); - } catch (ClassNotFoundException e) { - // do nothing - } - } - - mxHierarchicalLayout layout = new mxHierarchicalLayout(graph, SwingConstants.WEST); - layout.execute(graph.getDefaultParent()); - } - graph.getModel().endUpdate(); - } - - @Override - public void mouseClicked(MouseEvent e) { - Object cell = graphComponent.getCellAt(e.getX(), e.getY()); - - if ((e.getButton() == MouseEvent.BUTTON1) && (cell != null) && (cell instanceof ReferenceCell)) { - drawMind((ReferenceCell) cell); - } - - graphComponent.repaint(); - Platform.runLater(() -> topVBox.layout()); - } - - @Override - public void mousePressed(MouseEvent e) { - /* Nothing to do */ - } - - @Override - public void mouseReleased(MouseEvent e) { - /* Nothing to do */ - } - - @Override - public void mouseEntered(MouseEvent e) { - /* Nothing to do */ - } - - @Override - public void mouseExited(MouseEvent e) { - /* Nothing to do */ - } - - /** - * Event handler of OK button. - * - * @param event ActionEvent of this event. - */ - @FXML - private void onOkClick(ActionEvent event) { - - if ((currentSnapShotHeader.get() == null) - || (currentObjectTag.get() == -1)){ - return; - } else if (!currentSnapShotHeader.get().hasReferenceData()) { - // Version 1.0.x or "collect_reftree=false" do not have reftree data - Alert dialog = new Alert(AlertType.ERROR, resource.getString("reftree.nodata"), ButtonType.OK); - dialog.show(); - return; - } - - mxCell cell = (mxCell) graph.getDefaultParent(); - List removeCells = new ArrayList<>(); - - for (int i = 0; i < cell.getChildCount(); i++) { - mxCell removeCell = (mxCell) cell.getChildAt(i); - - if (removeCell.isEdge() || !((ReferenceCell) removeCell).isRoot()) { - removeCells.add(removeCell); - } else { - graph.removeSelectionCell(removeCell); - mxGeometry geometry = ((ReferenceCell) removeCell).getGeometry(); - geometry.setX(0); - geometry.setY(0); - ((ReferenceCell) removeCell).setGeometry(geometry); - } - - } - - graph.removeCells(removeCells.toArray()); - graph.refresh(); - graphComponent.refresh(); - graphComponent.getViewport().setViewPosition(new Point(0, 0)); - } - - /** - * Get property of current SnapShotHeader. - * - * @return Property of currentSnapShotHeader. - */ - public ObjectProperty currentSnapShotHeaderProperty() { - return currentSnapShotHeader; - } - - /** - * Get property of current Object tag. - * - * @return Property of currentObjectTag. - */ - public LongProperty currentObjectTagProperty() { - return currentObjectTag; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/ReferenceCell.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/ReferenceCell.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/* - * ReferenceCell.java - * Created on 2012/11/18 - * - * Copyright (C) 2011-2016 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs; - -import java.text.NumberFormat; -import com.mxgraph.model.mxCell; -import com.mxgraph.model.mxGeometry; -import com.mxgraph.util.mxConstants; -import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * extends {@link mxCell}
- *
- * Cells are the elements of the graph model. - */ -public class ReferenceCell extends mxCell { - - /** - * serialVersionUID. - */ - private static final long serialVersionUID = -4403355352725440764L; - - /** - * Defines the font size - */ - private static String FONT_SIZE; - /** - * Defines the zoom ratio by font size ratio. - */ - private static double ZOOM_RATIO; - - /** - * Defines the height of the cell. - */ - private static int CELL_HEIGHT; - - /** - * Define the width of the character in the cell. - */ - private static int CHAR_WIDTH; - - /** - * Information about the objects to display Map. - */ - private final ObjectData objectData; - - /** - * Holds information whether the cell as a starting point. - */ - private boolean rootCell; - - /** - * Constructs a new cell. - * - * @param data Information about the objects to display Map. - * @param root This cell is root or not. - * @param edge This cell is edge or not. - */ - public ReferenceCell(ObjectData data, boolean root, boolean edge) { - super(); - - objectData = data; - - if (edge) { - setEdge(true); - } else { - setValue(data.getName()); - setConnectable(false); - setVertex(true); - setStyle("fontSize="+FONT_SIZE); - - if (root) { - setStyle("shape=ellipse;fillColor=red;fontColor=black;fontSize="+FONT_SIZE); - } - - rootCell = root; - } - - setConnectable(false); - setGeometry(new mxGeometry(0, 0, CHAR_WIDTH * data.getName().length(), CELL_HEIGHT)); - } - - /** - * Initialize to define the size. - */ - public static void initialize() { - FONT_SIZE = String.valueOf(HeapStatsUtils.getFontSizeOfRefTree()); - ZOOM_RATIO = (double) HeapStatsUtils.getFontSizeOfRefTree() / mxConstants.DEFAULT_FONTSIZE; - CELL_HEIGHT = (int)(30 * ZOOM_RATIO); - CHAR_WIDTH = (int)(7 * ZOOM_RATIO); - } - - - /** - * Return the tag. - * - * @return Return the tag - */ - public final long getTag() { - return objectData.getTag(); - } - - /** - * It returns whether or not the cells to be the origin. - * - * @return It returns whether or not the cells to be the origin. - */ - public final boolean isRoot() { - return rootCell; - } - - @Override - public final String toString() { - NumberFormat format = NumberFormat.getInstance(); - StringBuilder buf = new StringBuilder(); - - if (isVertex()) { - buf.append(objectData.getLoaderName()); - buf.append(" (instances = "); - buf.append(format.format(objectData.getCount())); - buf.append("; heapsize = "); - buf.append(format.format(objectData.getTotalSize())); - buf.append(" bytes)"); - } else { - buf.append("instances = "); - buf.append(format.format(objectData.getCount())); - buf.append("; heapsize = "); - buf.append(format.format(objectData.getTotalSize())); - buf.append(" bytes"); - } - - return buf.toString(); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/ReferenceGraph.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/ReferenceGraph.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * ReferenceGraph.java - * Created on 2012/11/18 - * - * Copyright (C) 2011-2016 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs; - -import java.util.HashMap; -import java.util.Map; - -import com.mxgraph.util.mxConstants; -import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxStylesheet; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * extends {@link mxGraph}
- *
- * Implements a graph object that allows to create diagrams from a graph model - * and stylesheet. - */ -public class ReferenceGraph extends mxGraph { - - @Override - public final String getToolTipForCell(Object cell) { - - if (cell instanceof ReferenceCell) { - return ((ReferenceCell) cell).toString(); - } - - return super.getToolTipForCell(cell); - } - - /** - * Sets the style for edges. - */ - public final void setEdgeStyle() { - Map edgeStyle = new HashMap<>(); - edgeStyle.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_CONNECTOR); - edgeStyle.put(mxConstants.STYLE_ENDARROW, mxConstants.ARROW_DIAMOND); - edgeStyle.put(mxConstants.STYLE_STROKECOLOR, "#000000"); - edgeStyle.put(mxConstants.STYLE_FONTCOLOR, "#000000"); - edgeStyle.put(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_RIGHT); - edgeStyle.put(mxConstants.STYLE_FONTSIZE, HeapStatsUtils.getFontSizeOfRefTree()); - - mxStylesheet style = new mxStylesheet(); - style.setDefaultEdgeStyle(edgeStyle); - setStylesheet(style); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SnapshotController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SnapshotController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs; - -import java.net.URL; -import java.time.LocalDateTime; -import java.util.AbstractMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.stream.Collectors; -import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.LongProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.ObservableMap; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.chart.PieChart; -import javafx.scene.control.ComboBox; -import javafx.scene.control.SingleSelectionModel; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.cell.PropertyValueFactory; -import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; -import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.ChartColorManager; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotHeaderConverter; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class for "SnapShot Data" tab in SnapShot plugin. - */ -public class SnapshotController implements Initializable { - - @FXML - private ComboBox snapShotTimeCombo; - - @FXML - private TableView> snapShotSummaryTable; - - @FXML - private TableColumn, String> snapShotSummaryKey; - - @FXML - private TableColumn, String> snapShotSummaryValue; - - @FXML - private PieChart usagePieChart; - - @FXML - private TableColumn objColorColumn; - - @FXML - private TableView objDataTable; - - @FXML - private TableColumn objClassNameColumn; - - @FXML - private TableColumn objClassLoaderColumn; - - @FXML - private TableColumn objInstancesColumn; - - @FXML - private TableColumn objSizeColumn; - - private ObjectProperty> currentTarget; - - private BooleanProperty instanceGraph; - - private ObjectProperty>> topNList; - - private LongProperty currentObjectTag; - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); - instanceGraph = new SimpleBooleanProperty(); - topNList = new SimpleObjectProperty<>(); - currentObjectTag = new SimpleLongProperty(); - - snapShotTimeCombo.itemsProperty().bind(currentTarget); - - snapShotTimeCombo.setConverter(new SnapShotHeaderConverter()); - snapShotSummaryKey.setCellValueFactory(new PropertyValueFactory<>("key")); - snapShotSummaryValue.setCellValueFactory(new PropertyValueFactory<>("value")); - - objColorColumn.setCellFactory(p -> new TableCell() { - @Override - protected void updateItem(String item, boolean empty) { - super.updateItem(item, empty); - String style = Optional.ofNullable((ObjectData) getTableRow().getItem()) - .map(o -> "-fx-background-color: " + ChartColorManager.getCurrentColor(o.getName()) + ";") - .orElse("-fx-background-color: transparent;"); - setStyle(style); - } - }); - - objClassNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - objClassLoaderColumn.setCellValueFactory(new PropertyValueFactory<>("loaderName")); - objInstancesColumn.setCellValueFactory(new PropertyValueFactory<>("count")); - objInstancesColumn.setSortType(TableColumn.SortType.DESCENDING); - objSizeColumn.setCellValueFactory(new PropertyValueFactory<>("totalSize")); - objSizeColumn.setSortType(TableColumn.SortType.DESCENDING); - - currentObjectTag.bind(Bindings.createLongBinding(() -> Optional.ofNullable(objDataTable.getSelectionModel().getSelectedItem()) - .map(o -> o.getTag()) - .orElse(0xffffffffffffffffl), - objDataTable.getSelectionModel().selectedItemProperty())); - } - - /** - * Event handler of SnapShot TIme. - * - * @param event ActionEvent of this event. - */ - @FXML - @SuppressWarnings("unchecked") - private void onSnapShotTimeSelected(ActionEvent event) { - SnapShotHeader header = snapShotTimeCombo.getSelectionModel().getSelectedItem(); - if (header == null) { - return; - } - - ObservableList> summaryList = snapShotSummaryTable.getItems(); - summaryList.clear(); - usagePieChart.getData().clear(); - objDataTable.getItems().clear(); - ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); - - summaryList.addAll( - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.date"), header.getSnapShotDate().format(HeapStatsUtils.getDateTimeFormatter())), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.hasreftree"), header.hasReferenceData() ? "Yes" : "N/A"), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.entries"), Long.toString(header.getNumEntries())), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.instances"), Long.toString(header.getNumInstances())), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.heap"), String.format("%.02f MB", (double) (header.getNewHeap() + header.getOldHeap()) / 1024.0d / 1024.0d)), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.metaspace"), String.format("%.02f MB", (double) (header.getMetaspaceUsage()) / 1024.0d / 1024.0d)), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.cause"), header.getCauseString()), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.gccause"), header.getGcCause()), - new AbstractMap.SimpleEntry<>(resource.getString("snapshot.gctime"), String.format("%d ms", header.getGcTime()))); - - usagePieChart.getData().addAll(topNList.get().get(header.getSnapShotDate()).stream() - .map(o -> new PieChart.Data(o.getName(), instanceGraph.get() ? o.getCount() : o.getTotalSize())) - .collect(Collectors.toList())); - usagePieChart.getData().stream() - .forEach(d -> d.getNode().setStyle("-fx-pie-color: " + ChartColorManager.getNextColor(d.getName()))); - - objDataTable.setItems(FXCollections.observableArrayList( - header.getSnapShot(HeapStatsUtils.getReplaceClassName()).values().stream().collect(Collectors.toList()))); - objDataTable.getSortOrder().add(instanceGraph.get() ? objInstancesColumn : objSizeColumn); - } - - /** - * Get property of list of SnapShotHeader. - * - * @return Property of list of SnapShotHeader. - */ - public ObjectProperty> currentTargetProperty() { - return currentTarget; - } - - /** - * Get property for current SnapShot selection. This property is same as - * SnapShot time ComboBox. - * - * @return Property for current SnapShot selection. - */ - public ObjectProperty> snapshotSelectionModelProperty() { - return snapShotTimeCombo.selectionModelProperty(); - } - - /** - * Get property which contains chart category (instance count or object - * size). - * - * @return Property which contains chart category. - */ - public BooleanProperty instanceGraphProperty() { - return instanceGraph; - } - - /** - * Get property of Map of Top N list. - * - * @return Property of map of Top N list. - */ - public ObjectProperty>> topNListProperty() { - return topNList; - } - - /** - * Get property of current Object tag. - * - * @return Property of currentObjectTag. - */ - public LongProperty currentObjectTagProperty() { - return currentObjectTag; - } - - /** - * Get Object Data table. - * - * @return Object Data table. - */ - public TableView getObjDataTable() { - return objDataTable; - } - - /** - * Clear all items in SnapShot tab. - */ - public void clearAllItems(){ - snapShotSummaryTable.getItems().clear(); - usagePieChart.getData().clear(); - objDataTable.getItems().clear(); -} - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SummaryController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/SummaryController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,511 +0,0 @@ -/* - * Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.tabs; - -import java.net.URL; -import java.time.ZoneId; -import java.util.Locale; -import java.util.ResourceBundle; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.ObservableSet; -import javafx.concurrent.Task; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.chart.AreaChart; -import javafx.scene.chart.LineChart; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.StackedAreaChart; -import javafx.scene.chart.XYChart; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Label; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.Tooltip; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.GridPane; -import javafx.scene.paint.Color; -import javafx.scene.shape.Path; -import javafx.scene.shape.Rectangle; -import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; -import jp.co.ntt.oss.heapstats.container.snapshot.SummaryData; -import jp.co.ntt.oss.heapstats.utils.EpochTimeConverter; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * FXML Controller class for "Summary Data" tab in SnapShot plugin. - */ -public class SummaryController implements Initializable { - - @FXML - private TableView summaryTable; - - @FXML - private TableColumn keyColumn; - - @FXML - private TableColumn valueColumn; - - @FXML - private StackedAreaChart heapChart; - - private XYChart.Series youngUsage; - - private XYChart.Series oldUsage; - - private XYChart.Series free; - - @FXML - private LineChart instanceChart; - - private XYChart.Series instances; - - @FXML - private LineChart gcTimeChart; - - private XYChart.Series gcTime; - - @FXML - private AreaChart metaspaceChart; - - private XYChart.Series metaspaceUsage; - - private XYChart.Series metaspaceCapacity; - - private ObjectProperty summaryData; - - private ObjectProperty> currentTarget; - - private ObjectProperty> currentClassNameSet; - - private EpochTimeConverter epochTimeConverter; - - private Tooltip heapTooltip; - - private GridPane heapTooltipGrid; - - private Label youngLabel; - - private Label oldLabel; - - private Label freeLabel; - - private Tooltip metaspaceTooltip; - - private GridPane metaspaceTooltipGrid; - - private Label metaspaceUsageLabel; - - private Label metaspaceCapacityLabel; - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - summaryData = new SimpleObjectProperty<>(); - summaryData.addListener((v, o, n) -> setSummaryTable(n)); - currentTarget = new SimpleObjectProperty<>(FXCollections.emptyObservableList()); - currentClassNameSet = new SimpleObjectProperty<>(FXCollections.emptyObservableSet()); - - keyColumn.setCellValueFactory(new PropertyValueFactory<>("category")); - valueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); - - String bgcolor = "-fx-background-color: " + HeapStatsUtils.getChartBgColor() + ";"; - Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) - .peek(c -> c.lookup(".chart").setStyle(bgcolor)) - .forEach(c -> c.getXAxis().setTickMarkVisible(HeapStatsUtils.getTickMarkerSwitch())); - - epochTimeConverter = new EpochTimeConverter(); - Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) - .map(c -> (NumberAxis)c.getXAxis()) - .forEach(a -> a.setTickLabelFormatter(epochTimeConverter)); - - initializeChartSeries(); - } - - private void setSummaryTable(SummaryData data) { - if (data == null) { - summaryTable.getItems().clear(); - } else { - ResourceBundle resource = ResourceBundle.getBundle("snapshotResources", new Locale(HeapStatsUtils.getLanguage())); - String safepointTimeStr = data.hasSafepointTime() ? String.format("%d ms (%.02f %%)", data.getSafepointTime(), data.getSafepointPercentage()) - : "N/A"; - - summaryTable.setItems(FXCollections.observableArrayList(new SummaryData.SummaryDataEntry(resource.getString("summary.snapshot.count"), Integer.toString(data.getCount())), - new SummaryData.SummaryDataEntry(resource.getString("summary.gc.count"), String.format("%d (Full: %d, Young: %d)", data.getFullCount() + data.getYngCount(), data.getFullCount(), data.getYngCount())), - new SummaryData.SummaryDataEntry(resource.getString("summary.heap.usage"), String.format("%.1f MB", data.getLatestHeapUsage() / 1024.0d / 1024.0d)), - new SummaryData.SummaryDataEntry(resource.getString("summary.metaspace.usage"), String.format("%.1f MB", data.getLatestMetaspaceUsage() / 1024.0d / 1024.0d)), - new SummaryData.SummaryDataEntry(resource.getString("summary.gc.time"), String.format("%d ms", data.getMaxGCTime())), - new SummaryData.SummaryDataEntry(resource.getString("summary.gc.totaltime"), String.format("%d ms", data.getTotalGCTime())), - new SummaryData.SummaryDataEntry(resource.getString("summary.safepoint.time"), safepointTimeStr), - new SummaryData.SummaryDataEntry(resource.getString("summary.snapshot.size"), String.format("%.1f KB", data.getMaxSnapshotSize() / 1024.0d)), - new SummaryData.SummaryDataEntry(resource.getString("summary.snapshot.entrycount"), Long.toString(data.getMaxEntryCount())) - )); - } - } - - /** - * Initialize Series in Chart. This method uses to avoid RuntimeException - * which is related to: RT-37994: [FXML] ProxyBuilder does not support - * read-only collections https://javafx-jira.kenai.com/browse/RT-37994 - */ - @SuppressWarnings("unchecked") - private void initializeChartSeries() { - youngUsage = new XYChart.Series<>(); - youngUsage.setName("Young"); - oldUsage = new XYChart.Series<>(); - oldUsage.setName("Old"); - free = new XYChart.Series<>(); - free.setName("Free"); - - String cssName = "/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/tabs/"; - if (HeapStatsUtils.getHeapOrder()) { - heapChart.getData().addAll(youngUsage, oldUsage, free); - cssName += "heapsummary-bottom-young.css"; - } else { - heapChart.getData().addAll(oldUsage, youngUsage, free); - cssName += "heapsummary-bottom-old.css"; - } - heapChart.getStylesheets().add(cssName); - - instances = new XYChart.Series<>(); - instances.setName("Instances"); - instanceChart.getData().add(instances); - - gcTime = new XYChart.Series<>(); - gcTime.setName("GC Time"); - gcTimeChart.getData().add(gcTime); - - metaspaceCapacity = new XYChart.Series<>(); - metaspaceCapacity.setName("Capacity"); - metaspaceUsage = new XYChart.Series<>(); - metaspaceUsage.setName("Usage"); - metaspaceChart.getData().addAll(metaspaceCapacity, metaspaceUsage); - - /* Tooltip setup */ - /* Java heap */ - heapTooltipGrid = new GridPane(); - heapTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); - youngLabel = new Label(); - oldLabel = new Label(); - freeLabel = new Label(); - Rectangle youngRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle oldRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle freeRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - - Platform.runLater(() -> { - youngRect.setStyle("-fx-fill: " + ((Path)heapChart.lookup(".series0")).getFill().toString().replace("0x", "#")); - oldRect.setStyle("-fx-fill: " + ((Path)heapChart.lookup(".series1")).getFill().toString().replace("0x", "#")); - freeRect.setStyle("-fx-fill: " + ((Path)heapChart.lookup(".series2")).getFill().toString().replace("0x", "#")); - }); - - heapTooltipGrid.add(youngRect, 0, 0); - heapTooltipGrid.add(new Label("Young"), 1, 0); - heapTooltipGrid.add(youngLabel, 2, 0); - heapTooltipGrid.add(oldRect, 0, 1); - heapTooltipGrid.add(new Label("Old"), 1, 1); - heapTooltipGrid.add(oldLabel, 2, 1); - heapTooltipGrid.add(freeRect, 0, 2); - heapTooltipGrid.add(new Label("Free"), 1, 2); - heapTooltipGrid.add(freeLabel, 2, 2); - heapTooltip = new Tooltip(); - heapTooltip.setGraphic(heapTooltipGrid); - heapTooltip.setContentDisplay(ContentDisplay.BOTTOM); - - /* Metaspace */ - metaspaceTooltipGrid = new GridPane(); - metaspaceTooltipGrid.setHgap(HeapStatsUtils.TOOLTIP_GRIDPANE_GAP); - metaspaceUsageLabel = new Label(); - metaspaceCapacityLabel = new Label(); - Rectangle metaUsageRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - Rectangle metaCapacityRect = new Rectangle(HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE, HeapStatsUtils.TOOLTIP_LEGEND_RECT_SIZE); - - Platform.runLater(() -> { - metaUsageRect.setStyle("-fx-fill: " + ((Path)metaspaceChart.lookup(".default-color0.chart-series-area-fill")).getFill().toString().replace("0x", "#")); - metaCapacityRect.setStyle("-fx-fill: " + ((Path)metaspaceChart.lookup(".default-color1.chart-series-area-fill")).getFill().toString().replace("0x", "#")); - }); - - metaspaceTooltipGrid.add(metaUsageRect, 0, 0); - metaspaceTooltipGrid.add(new Label("Usage"), 1, 0); - metaspaceTooltipGrid.add(metaspaceUsageLabel, 2, 0); - metaspaceTooltipGrid.add(metaCapacityRect, 0, 1); - metaspaceTooltipGrid.add(new Label("Capacity"), 1, 1); - metaspaceTooltipGrid.add(metaspaceCapacityLabel, 2, 1); - metaspaceTooltip = new Tooltip(); - metaspaceTooltip.setGraphic(metaspaceTooltipGrid); - metaspaceTooltip.setContentDisplay(ContentDisplay.BOTTOM); - } - - /** - * JavaFX task class for calculating GC summary. - */ - private class CalculateGCSummaryTask extends Task { - - private int processedIndex; - - private final Consumer> drawRebootSuspectLine; - - /* Java Heap Usage Chart */ - private final ObservableList> youngUsageBuf; - private final ObservableList> oldUsageBuf; - private final ObservableList> freeBuf; - - /* Instances */ - private final ObservableList> instanceBuf; - - /* GC time Chart */ - private final ObservableList> gcTimeBuf; - - /* Metaspace Chart */ - private final ObservableList> metaspaceUsageBuf; - private final ObservableList> metaspaceCapacityBuf; - - /** - * Constructor of CalculateGCSummaryTask. - * - * @param drawRebootSuspectLine Consumer for drawing reboot line. This - * consumer is called in Platform#runLater() at succeeded(). - */ - public CalculateGCSummaryTask(Consumer> drawRebootSuspectLine) { - this.drawRebootSuspectLine = drawRebootSuspectLine; - - youngUsageBuf = FXCollections.observableArrayList(); - oldUsageBuf = FXCollections.observableArrayList(); - freeBuf = FXCollections.observableArrayList(); - instanceBuf = FXCollections.observableArrayList(); - gcTimeBuf = FXCollections.observableArrayList(); - metaspaceUsageBuf = FXCollections.observableArrayList(); - metaspaceCapacityBuf = FXCollections.observableArrayList(); - } - - private void processSnapShotHeader(SnapShotHeader header) { - long time = header.getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); - - youngUsageBuf.add(new XYChart.Data<>(time, header.getNewHeap() / 1024 / 1024)); - oldUsageBuf.add(new XYChart.Data<>(time, header.getOldHeap() / 1024 / 1024)); - freeBuf.add(new XYChart.Data<>(time, (header.getTotalCapacity() - header.getNewHeap() - header.getOldHeap()) / 1024 / 1024)); - - instanceBuf.add(new XYChart.Data<>(time, header.getNumInstances())); - - gcTimeBuf.add(new XYChart.Data<>(time, header.getGcTime())); - - metaspaceUsageBuf.add(new XYChart.Data<>(time, header.getMetaspaceUsage() / 1024 / 1024)); - metaspaceCapacityBuf.add(new XYChart.Data<>(time, header.getMetaspaceCapacity() / 1024 / 1024)); - - currentClassNameSet.get().addAll(header.getSnapShot(HeapStatsUtils.getReplaceClassName()) - .values() - .stream() - .map(s -> s.getName()) - .collect(Collectors.toSet())); - - updateProgress(++processedIndex, currentTarget.get().size()); - } - - @Override - protected Void call() throws Exception { - updateMessage("Calcurating GC summary..."); - processedIndex = 0; - currentTarget.get().stream() - .forEachOrdered(d -> processSnapShotHeader(d)); - return null; - } - - private void setupJavaHeapChartTooltip(int idx){ - XYChart.Data youngNode = youngUsage.getData().get(idx); - XYChart.Data oldNode = oldUsage.getData().get(idx); - XYChart.Data freeNode = free.getData().get(idx); - long youngInMB = youngNode.getYValue(); - long oldInMB = oldNode.getYValue(); - long freeInMB = freeNode.getYValue(); - - EventHandler handler = e -> { - heapTooltip.setText(epochTimeConverter.toString(youngNode.getXValue())); - youngLabel.setText(youngInMB + " MB"); - oldLabel.setText(oldInMB + " MB"); - freeLabel.setText(freeInMB + " MB"); - }; - - Tooltip.install(youngNode.getNode(), heapTooltip); - youngNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(oldNode.getNode(), heapTooltip); - oldNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(freeNode.getNode(), heapTooltip); - freeNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - } - - private void setupMetaspaceChartTooltip(int idx){ - XYChart.Data usageNode = metaspaceUsage.getData().get(idx); - XYChart.Data capacityNode = metaspaceCapacity.getData().get(idx); - long usage = usageNode.getYValue(); - long capacity = capacityNode.getYValue(); - - EventHandler handler = e -> { - metaspaceTooltip.setText(epochTimeConverter.toString(usageNode.getXValue())); - metaspaceUsageLabel.setText(usage + " MB"); - metaspaceCapacityLabel.setText(capacity + " MB"); - }; - - Tooltip.install(usageNode.getNode(), metaspaceTooltip); - usageNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - Tooltip.install(capacityNode.getNode(), metaspaceTooltip); - capacityNode.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, handler); - } - - @Override - protected void succeeded() { - long startEpoch = currentTarget.get().get(0).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); - long endEpoch = currentTarget.get().get(currentTarget.get().size() - 1).getSnapShotDate().atZone(ZoneId.systemDefault()).toEpochSecond(); - Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) - .peek(c -> ((NumberAxis)c.getXAxis()).setTickUnit((endEpoch - startEpoch) / HeapStatsUtils.getXTickUnit())) - .peek(c -> ((NumberAxis)c.getXAxis()).setLowerBound(startEpoch)) - .peek(c -> ((NumberAxis)c.getXAxis()).setUpperBound(endEpoch)) - .forEach(c -> Platform.runLater(() -> drawRebootSuspectLine.accept(c))); - - /* Replace new chart data */ - youngUsage.setData(youngUsageBuf); - oldUsage.setData(oldUsageBuf); - free.setData(freeBuf); - - instances.setData(instanceBuf); - - gcTime.setData(gcTimeBuf); - - metaspaceUsage.setData(metaspaceUsageBuf); - metaspaceCapacity.setData(metaspaceCapacityBuf); - - /* Set tooltip */ - /* Java Heap & Metaspace */ - IntStream.range(0, currentTarget.get().size()) - .peek(this::setupJavaHeapChartTooltip) - .forEach(this::setupMetaspaceChartTooltip); - - Tooltip tooltip = new Tooltip(); - - /* Insatances */ - instances.getData() - .stream() - .peek(d -> Tooltip.install(d.getNode(), tooltip)) - .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText((epochTimeConverter.toString(d.getXValue()) + ": " + d.getYValue())))); - - /* GC time */ - gcTime.getData() - .stream() - .peek(d -> Tooltip.install(d.getNode(), tooltip)) - .forEach(d -> d.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> tooltip.setText(String.format("%s: %d ms", epochTimeConverter.toString(d.getXValue()), d.getYValue())))); - } - - } - - /** - * Get property of SummaryData. - * - * @return Property of SummaryData. - */ - public ObjectProperty summaryDataProperty() { - return summaryData; - } - - /** - * Get property of list of SnapShotHeader. - * - * @return Property of list of SnapShotHeader. - */ - public ObjectProperty> currentTargetProperty() { - return currentTarget; - } - - /** - * Get property of class name set. - * - * @return Property of class name set. - */ - public ObjectProperty> currentClassNameSetProperty() { - return currentClassNameSet; - } - - /** - * Get new task for calculating GC summary. - * - * @param drawRebootSuspectLine Consumer for drawing reboot line. - * @return Task for calculating GC summary. - */ - public Task getCalculateGCSummaryTask(Consumer> drawRebootSuspectLine) { - return new CalculateGCSummaryTask(drawRebootSuspectLine); - } - - /** - * Get Java heap chart. - * - * @return Java heap chart. - */ - public StackedAreaChart getHeapChart() { - return heapChart; - } - - /** - * Get instance chart. - * - * @return Instance chart. - */ - public LineChart getInstanceChart() { - return instanceChart; - } - - /** - * Get GC time chart. - * - * @return GC time chart. - */ - public LineChart getGcTimeChart() { - return gcTimeChart; - } - - /** - * Get Metaspace chart. - * - * @return Metaspace chart. - */ - public AreaChart getMetaspaceChart() { - return metaspaceChart; - } - - /** - * Clear all items in SnapShot Summary tab. - */ - public void clearAllItems(){ - summaryTable.getItems().clear(); - - Stream.of(heapChart, instanceChart, gcTimeChart, metaspaceChart) - .peek(c -> c.getData().stream().forEach(s -> s.getData().clear())) // Clear chart series data. - .flatMap(c -> c.getParent().getChildrenUnmodifiable().stream()) // Get parent node of chart. - .filter(n -> n instanceof AnchorPane) - .forEach(p -> ((AnchorPane)p).getChildren().clear()); // Chart reboot suspect lines. -} - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2015-2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.event.ActionEvent; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.*; -import javafx.scene.control.cell.CheckBoxTableCell; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.stage.FileChooser; -import jp.co.ntt.oss.heapstats.MainWindowController; -import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; -import jp.co.ntt.oss.heapstats.plugin.PluginController; -import jp.co.ntt.oss.heapstats.task.ThreadRecordParseTask; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; -import jp.co.ntt.oss.heapstats.utils.TaskAdapter; - -import java.io.File; -import java.net.URL; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.stream.Collectors; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; - - -/** - * FXML Controller class - * - * @author Yasumasa Suenaga - */ -public class ThreadRecorderController extends PluginController implements Initializable { - - private static final int TIMELINE_PADDING = 8; - - @FXML - private TextField fileNameBox; - - @FXML - private Label startTimeLabel; - - @FXML - private Label endTimeLabel; - - @FXML - private TableColumn showColumn; - - @FXML - private TableColumn threadNameColumn; - - @FXML - private TableView timelineView; - - @FXML - private TableColumn> timelineColumn; - - @FXML - private SplitPane rangePane; - - @FXML - private Button okBtn; - - private List threadStatList; - - private Map idMap; - - private ObjectProperty rangeStart; - - private ObjectProperty rangeEnd; - - /** - * Update caption of label which represents time of selection. - * - * @param target Label compornent to draw. - * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 . - */ - private void updateRangeLabel(Label target, double newValue){ - if((threadStatList != null) && !threadStatList.isEmpty()){ - LocalDateTime start = threadStatList.get(0).getTime(); - LocalDateTime end = threadStatList.get(threadStatList.size() - 1).getTime(); - long diff = start.until(end, ChronoUnit.MILLIS); - LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS); - - if(target == startTimeLabel){ - rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS)); - } - else{ - rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS)); - } - - target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter())); - } - } - - /** - * Initializes the controller class. - */ - @Override - public void initialize(URL url, ResourceBundle rb) { - threadStatList = null; - rangeStart = new SimpleObjectProperty<>(); - rangeEnd = new SimpleObjectProperty<>(); - - rangePane.getDividers().get(0).positionProperty().addListener((b, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue())); - rangePane.getDividers().get(1).positionProperty().addListener((b, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue())); - - showColumn.setCellValueFactory(new PropertyValueFactory<>("show")); - showColumn.setCellFactory(CheckBoxTableCell.forTableColumn(showColumn)); - showColumn.setSortType(TableColumn.SortType.DESCENDING); - threadNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - timelineColumn.setCellValueFactory(new PropertyValueFactory<>("threadStats")); - timelineColumn.setCellFactory(param -> new TimelineCell(rangeStart, rangeEnd)); - timelineColumn.prefWidthProperty().bind(timelineView.widthProperty() - .subtract(showColumn.widthProperty()) - .subtract(threadNameColumn.widthProperty()) - .subtract(TIMELINE_PADDING)); - rangePane.getItems().forEach(n -> SplitPane.setResizableWithParent(n, false)); - } - - /** - * Event handler for open button. - * @param event Pushing open button event. - */ - @FXML - public void onOpenBtnClick(ActionEvent event){ - FileChooser dialog = new FileChooser(); - ResourceBundle resource = ResourceBundle.getBundle("threadrecorderResources", new Locale(HeapStatsUtils.getLanguage())); - - dialog.setTitle(resource.getString("dialog.filechooser.title")); - dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); - dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Thread Recorder file (*.htr)", "*.htr"), - new FileChooser.ExtensionFilter("All files", "*.*")); - File recorderFile = dialog.showOpenDialog(MainWindowController.getInstance().getOwner()); - - if(recorderFile != null){ - timelineView.getItems().clear(); - HeapStatsUtils.setDefaultDirectory(recorderFile.getParent()); - fileNameBox.setText(recorderFile.getAbsolutePath()); - - TaskAdapter task = new TaskAdapter<>(new ThreadRecordParseTask(recorderFile)); - super.bindTask(task); - task.setOnSucceeded(evt -> { - ThreadRecordParseTask parser = task.getTask(); - idMap = parser.getIdMap(); - threadStatList = parser.getThreadStatList(); - rangePane.getDividers().get(0).setPosition(0.0d); - rangePane.getDividers().get(1).setPosition(1.0d); - - rangePane.setDisable(false); - okBtn.setDisable(false); - - // FIX ME! Can we redraw more lightly? - timelineColumn.prefWidthProperty().addListener((b, o, n) -> onOkBtnClick(null)); - }); - - Thread parseThread = new Thread(task); - parseThread.start(); - } - - } - - /** - * Event handler for OK button. - * @param event Pushing ok button event. - */ - @FXML - private void onOkBtnClick(ActionEvent event){ - Map> statById = threadStatList.stream() - .filter(s -> s.getTime().isAfter(rangeStart.get()) && s.getTime().isBefore(rangeEnd.get())) - .collect(Collectors.groupingBy(ThreadStat::getId)); - ObservableList threadStats = FXCollections.observableArrayList(idMap.keySet().stream() - .sorted() - .map(k -> new ThreadStatViewModel(k, idMap.get(k), rangeStart.get(), rangeEnd.get(), statById.get(k))) - .collect(Collectors.toList())); - timelineView.setItems(threadStats); - timelineView.getSortOrder().add(showColumn); - } - - @Override - public String getPluginName() { - return "Thread Recorder"; - } - - @Override - public String getLicense() { - return PluginController.LICENSE_GPL_V2; - } - - @Override - public Map getLibraryLicense() { - return null; - } - - @Override - public EventHandler getOnPluginTabSelected() { - return null; - } - - @Override - public Runnable getOnCloseRequest() { - return null; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadStatViewModel.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadStatViewModel.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder; - -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.beans.property.SimpleBooleanProperty; -import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * Thread Status Data Model for presentation. - * - * @author Yasumasa Suenaga - */ -public class ThreadStatViewModel { - - private final BooleanProperty show; - - private long id; - - private String name; - - private LocalDateTime startTime; - - private LocalDateTime endTime; - - private final ReadOnlyObjectWrapper> threadStats; - - /** - * Constructor of ThreadStatViewModel - * - * @param id Thread ID - * @param name Thread Name - * @param startTime Start time - * @param endTime End time - * @param threadStats List of ThreadStat to draw. - */ - public ThreadStatViewModel(long id, String name, LocalDateTime startTime, LocalDateTime endTime, - List threadStats) { - this.id = id; - this.name = name; - this.startTime = startTime; - this.endTime = endTime; - this.threadStats = new ReadOnlyObjectWrapper<>(threadStats); - - if((threadStats == null) || threadStats.isEmpty()){ - this.show = new SimpleBooleanProperty(false); - } - else{ - this.show = new SimpleBooleanProperty(true); - } - - } - - /** - * Get show property. - * - * @return show property. - */ - public BooleanProperty showProperty(){ - return show; - } - - /** - * Get Thread ID. - * - * @return Thread ID. - */ - public long getId() { - return id; - } - - /** - * Set Thread ID. - * @param id New Thread ID. - */ - public void setId(long id) { - this.id = id; - } - - /** - * Get Thread name. - * @return Thread name. - */ - public String getName() { - return name; - } - - /** - * Set Thread name. - * @param name New Thread name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Get Start time of Thread Recorder events. - * - * @return Start time. - */ - public LocalDateTime getStartTime() { - return startTime; - } - - /** - * Set Start time of Thread Recorder events. - * - * @param startTime New start time. - */ - public void setStartTime(LocalDateTime startTime) { - this.startTime = startTime; - } - - /** - * Get End time of Thread Recorder events. - * - * @return End time. - */ - public LocalDateTime getEndTime() { - return endTime; - } - - /** - * Set End time of Thread Recorder events. - * - * @param endTime New end time. - */ - public void setEndTime(LocalDateTime endTime) { - this.endTime = endTime; - } - - /** - * Get list of ThreadStat. - * - * @return ThreadStat list. - */ - public List getThreadStats() { - return threadStats.get(); - } - - /** - * Get threadStats property. - * @return threadStats property. - */ - public ReadOnlyObjectProperty> threadStatsProperty() { - return threadStats.getReadOnlyProperty(); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/TimelineCell.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/TimelineCell.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2015 Takashi Aoe - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder; - -import javafx.geometry.Pos; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.TableCell; -import javafx.scene.layout.HBox; -import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; - -import java.time.LocalDateTime; -import java.util.List; -import javafx.beans.property.ObjectProperty; - -/** - * Table cell for thread timeline. - */ -public class TimelineCell extends TableCell> { - - private final ObjectProperty rangeStart; - - private final ObjectProperty rangeEnd; - - /** - * Constructor of TielineCell. - * - * @param rangeStart Start time. - * @param rangeEnd End time. - */ - public TimelineCell(ObjectProperty rangeStart, ObjectProperty rangeEnd) { - setContentDisplay(ContentDisplay.GRAPHIC_ONLY); - setAlignment(Pos.CENTER_LEFT); - - this.rangeStart = rangeStart; - this.rangeEnd = rangeEnd; - } - - @Override - protected void updateItem(List item, boolean empty) { - super.updateItem(item, empty); - ThreadStatViewModel model = (ThreadStatViewModel)getTableRow().getItem(); - - if (empty || (item == null) || item.isEmpty() || (model == null)) { - updateToEmptyCell(); - } else { - drawTimeline(model); - } - - } - - private void drawTimeline(ThreadStatViewModel viewModel) { - TimelineGenerator generator = new TimelineGenerator(viewModel, getTableColumn().prefWidthProperty()); - HBox container = generator.createTimeline(rangeStart.get(), rangeEnd.get()); - - container.visibleProperty().bind(viewModel.showProperty()); - setGraphic(container); - } - - private void updateToEmptyCell() { - setText(null); - setGraphic(null); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/TimelineGenerator.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/TimelineGenerator.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2015 Takashi Aoe - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalUnit; -import java.util.ArrayList; -import java.util.List; -import javafx.beans.property.DoubleProperty; -import javafx.geometry.Pos; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; -import javafx.scene.shape.Rectangle; -import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; - -/** - * Generate Thread Recorder timeline. - * - * @author Yasumasa Suenaga - */ -public class TimelineGenerator { - - private static final double RECT_HEIGHT = 16; - - private static final String CSS_CLASS_PREFIX = "rect-"; - - private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"); - - public static enum ThreadEvent{ - Unused, - Run, - MonitorWait, - MonitorContended, - ThreadSleep, - Park, - FileWrite, - FileRead, - SocketWrite, - SocketRead - } - - private final ThreadStatViewModel viewModel; - - private final DoubleProperty prefWidth; - - private long range; - - private double scale; - - private TemporalUnit unit; - - /** - * Constructor of TimelineGenerator. - * - * @param viewModel ViewModel to draw. - * @param prefWidth prefWidth to bind. - */ - public TimelineGenerator(ThreadStatViewModel viewModel, DoubleProperty prefWidth) { - this.viewModel = viewModel; - this.prefWidth = prefWidth; - } - - private void decideDrawScale(LocalDateTime start, LocalDateTime end){ - double width = prefWidth.get(); - - // Try to milli sec - unit = ChronoUnit.MILLIS; - range = start.until(end, unit); - scale = width / (double)range; - - // Try to micro sec - if(scale > 1.0d){ - unit = ChronoUnit.MICROS; - range = start.until(end, unit); - scale = width / (double)range; - - // Try to nano sec - if(scale > 1.0d){ - unit = ChronoUnit.NANOS; - range = start.until(end, unit); - scale = width / (double)range; - } - - } - - } - - /** - * Create timeline cells. - * - * @return HBox which includes Thread Recorder events. - */ - public HBox createTimeline(){ - List threadStatList = viewModel.threadStatsProperty().get(); - - if(threadStatList.isEmpty()){ - return new HBox(); - } - - LocalDateTime start = threadStatList.get(0).getTime(); - LocalDateTime end = threadStatList.get(threadStatList.size() - 1).getTime(); - - return createTimeline(start, end); - } - - /** - * Create timeline cells. - * - * @param start Start time. - * @param end End time. - * @return HBox which includes Thread Recorder events. - */ - public HBox createTimeline(LocalDateTime start, LocalDateTime end){ - HBox container = new HBox(); - container.setAlignment(Pos.CENTER_LEFT); - container.prefWidthProperty().bind(prefWidth); - - decideDrawScale(start, end); - drawTimeline(start, container); - - return container; - } - - private ThreadEvent convertToThreadEvent(ThreadStat.ThreadEvent event){ - switch (event) { - case ThreadStart: - case MonitorWaited: - case MonitorContendedEntered: - case ThreadSleepEnd: - case Unpark: - case FileWriteEnd: - case FileReadEnd: - case SocketWriteEnd: - case SocketReadEnd: - return ThreadEvent.Run; - - case ThreadEnd: - return ThreadEvent.Unused; - - case MonitorWait: - return ThreadEvent.MonitorWait; - - case MonitorContendedEnter: - return ThreadEvent.MonitorContended; - - case ThreadSleepStart: - return ThreadEvent.ThreadSleep; - - case Park: - return ThreadEvent.Park; - - case FileWriteStart: - return ThreadEvent.FileWrite; - - case FileReadStart: - return ThreadEvent.FileRead; - - case SocketWriteStart: - return ThreadEvent.SocketWrite; - - case SocketReadStart: - return ThreadEvent.SocketRead; - - default: - return ThreadEvent.Unused; - } - } - - private void drawTimeline(LocalDateTime start, HBox container) { - List rects = new ArrayList<>(); - int startIndex; - LocalDateTime prevTime; - ThreadEvent prevEvent; - long prevAdditionalData; - List threadStatList = viewModel.threadStatsProperty().get(); - - if(threadStatList.isEmpty()){ - return; - } - else{ - ThreadStat threadStat = threadStatList.get(0); - prevTime = threadStat.getTime(); - - if(start.equals(prevTime)){ - startIndex = 1; - prevTime = threadStat.getTime(); - prevEvent = convertToThreadEvent(threadStat.getEvent()); - prevAdditionalData = threadStat.getAdditionalData(); - } - else{ - startIndex = 0; - prevTime = start; - prevEvent = ThreadEvent.Unused; - prevAdditionalData = 0; - } - - } - - Rectangle fusionRect = new Rectangle(0, RECT_HEIGHT); - fusionRect.getStyleClass().add(CSS_CLASS_PREFIX + "fusion"); - - for(int Cnt = startIndex; Cnt < threadStatList.size(); Cnt++){ - ThreadStat threadStat = threadStatList.get(Cnt); - long additionalData; - - if((prevEvent == ThreadEvent.FileRead) || (prevEvent == ThreadEvent.FileWrite) || - (prevEvent == ThreadEvent.SocketRead) || (prevEvent == ThreadEvent.SocketWrite)){ - additionalData = threadStat.getAdditionalData(); - } - else{ - additionalData = prevAdditionalData; - } - - Rectangle rect = createThreadRect(prevTime, threadStat.getTime(), prevEvent, additionalData); - - if(rect.getWidth() < 1.0d){ - fusionRect.setWidth(fusionRect.getWidth() + rect.getWidth()); - } - else{ - - if(fusionRect.getWidth() > 0.0d){ - rects.add(fusionRect); - fusionRect = new Rectangle(0, RECT_HEIGHT); - fusionRect.getStyleClass().add(CSS_CLASS_PREFIX + "fusion"); - Tooltip.install(fusionRect, new Tooltip("Very short time event.")); - } - - rects.add(rect); - } - - prevTime = threadStat.getTime(); - prevAdditionalData = threadStat.getAdditionalData(); - prevEvent = convertToThreadEvent(threadStat.getEvent()); - } - - if(fusionRect.getWidth() > 0.0d){ - rects.add(fusionRect); - Tooltip.install(fusionRect, new Tooltip("Very short time event.")); - } - - container.getChildren().addAll(rects); - } - - private Rectangle createThreadRect(LocalDateTime startTime, LocalDateTime endTime, ThreadEvent event, long additionalData) { - long timeDiff = startTime.until(endTime, unit); - double width = scale * timeDiff; - Rectangle rectangle = new Rectangle(width, RECT_HEIGHT); - String styleClass = CSS_CLASS_PREFIX + event.name().toLowerCase(); - rectangle.getStyleClass().add(styleClass); - - if(width > 1.0d){ - String caption = startTime.format(formatter) + " - " + endTime.format(formatter) + ": " + event.toString(); - switch(event){ - case MonitorWait: - case ThreadSleep: - case Park: - if(additionalData > 0){ - caption += " (" + Long.toString(additionalData) + " ms)"; - } - break; - - case FileWrite: - case FileRead: - case SocketWrite: - case SocketRead: - caption += " (" + Long.toString(additionalData) + " bytes)"; - break; - } - - if((event != ThreadEvent.Unused)){ - Tooltip.install(rectangle, new Tooltip(caption)); - } - - } - - return rectangle; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/EpochTimeConverter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/EpochTimeConverter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2016 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.utils; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import javafx.util.StringConverter; - -/** - * String converter for epoch time. - * This class supports SECOND time unit ONLY. - * - * @author Yasumasa Suenaga - */ -public class EpochTimeConverter extends StringConverter{ - - @Override - public String toString(Number object) { - return LocalDateTime.ofInstant(Instant.ofEpochSecond(object.longValue()), ZoneId.systemDefault()).format(HeapStatsUtils.getDateTimeFormatter()); - } - - @Override - public Number fromString(String string) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/TaskAdapter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/TaskAdapter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2015 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.utils; - -import javafx.application.Platform; -import javafx.concurrent.Task; -import jp.co.ntt.oss.heapstats.task.ProgressRunnable; - -/** - * Adapter class for Task in Java FX. - * This class adapt ProgressRunnable to Java FX Void Task. - * - * @author Yasumasa Suenaga - * @param Implementation of ProgressRunnable - */ -public class TaskAdapter extends Task{ - - private final T task; - - /** - * Constructor of TaskAdapter. - * - * @param task Instance of ProgressRunnable - */ - public TaskAdapter(T task){ - this.task = task; - this.task.setUpdateProgress(p -> updateProgress(p, task.getTotal())); - } - - /** - * Get ProgressRunnable instance which is related to this task. - * @return Instance of ProgressRunnable - */ - public T getTask() { - return task; - } - - @Override - protected Void call() throws Exception { - - try{ - task.run(); - } - catch(Exception e){ - Platform.runLater(() -> HeapStatsUtils.showExceptionDialog(e)); - } - - return null; - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/ThreadStatConverter.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/ThreadStatConverter.java Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2015-2016 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package jp.co.ntt.oss.heapstats.utils; - -import javafx.util.StringConverter; -import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; - -/** - * StringConverter for LocalDateTime of SnapShotHeader. - * This class is used at JavaFX controls. - * - * @author Yasumasa Suenaga - */ -public class ThreadStatConverter extends StringConverter{ - - @Override - public String toString(ThreadStat object) { - return object.getTime().format(HeapStatsUtils.getDateTimeFormatter()); - } - - @Override - public ThreadStat fromString(String string) { - throw new UnsupportedOperationException("ThreadStat DO NOT convert from String."); - } - -} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/java/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/module-info.java Fri Sep 27 14:47:03 2019 +0900 @@ -0,0 +1,47 @@ +/* + * module-info.java + * + * Copyright (C) 2019 Yasumasa Suenaga + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +module heapstats.fx { + requires java.logging; + requires java.rmi; // for JMX + requires java.xml.bind; + + requires heapstats.jmx; + requires heapstats.core; + requires heapstats.plugin.api; + + requires javafx.fxml; + requires javafx.controls; + requires javafx.swing; + + // JGraphX has automatic module name: + // https://github.com/jgraph/jgraphx/pull/93 + requires com.mxgraph.jgraphx; + + exports jp.co.ntt.oss.heapstats.fx to javafx.graphics; + opens jp.co.ntt.oss.heapstats.fx to javafx.fxml; + opens jp.co.ntt.oss.heapstats.fx.plugin.builtin.log to javafx.fxml; + opens jp.co.ntt.oss.heapstats.fx.plugin.builtin.log.tabs to javafx.fxml; + opens jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot to javafx.fxml; + opens jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs to javafx.fxml; + opens jp.co.ntt.oss.heapstats.fx.plugin.builtin.threadrecorder to javafx.fxml; + opens jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive to javafx.fxml; +} diff -r 65602e1c057d -r dd85c1cbc8c8 analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/aboutDialog.fxml --- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/aboutDialog.fxml Thu Jul 25 00:06:16 2019 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - -