├── .gitignore ├── .travis.yml ├── COPYRIGHT ├── QUICK-START.md ├── README.md ├── bin └── inspector ├── docs ├── features.md ├── filters.md ├── flame-graph.png ├── hooks.md ├── hooks │ ├── datagram-socket-factory.md │ ├── io-trace.md │ ├── selector-provider.md │ └── socket-factory.md ├── index.md ├── probe.md ├── probe │ ├── event-handling.md │ ├── filter-properties.md │ ├── jmx-cpu-stats.md │ ├── jmx-file-io.md │ ├── jmx-socket-io.md │ ├── jmx-unclosed-socket.md │ ├── properties-cpu-stats.md │ ├── properties-datagram-io.md │ ├── properties-file-io.md │ ├── properties-socket-io.md │ └── properties-unclosed-socket.md ├── root-graph.png ├── run.md └── types │ ├── cpu-stats.md │ ├── datagram-io.md │ ├── file-io.md │ ├── socket-io.md │ └── socket-unclosed.md ├── gumshoe-probes ├── .gitignore ├── pom.xml ├── src │ ├── main │ │ └── java │ │ │ ├── com │ │ │ └── dell │ │ │ │ └── gumshoe │ │ │ │ ├── Agent.java │ │ │ │ ├── Probe.java │ │ │ │ ├── ProbeManager.java │ │ │ │ ├── file │ │ │ │ ├── FileIOAccumulator.java │ │ │ │ ├── FileIODetailAdder.java │ │ │ │ ├── FileIOEvent.java │ │ │ │ ├── FileIOMonitor.java │ │ │ │ ├── FileIOProbe.java │ │ │ │ ├── FileMatcher.java │ │ │ │ ├── FileMatcherSeries.java │ │ │ │ ├── FileReadEvent.java │ │ │ │ ├── FileWriteEvent.java │ │ │ │ └── PathPatternMatcher.java │ │ │ │ ├── hook │ │ │ │ ├── IoTraceAdapter.java │ │ │ │ ├── IoTraceHandler.java │ │ │ │ ├── IoTraceListener.java │ │ │ │ └── IoTraceMultiplexer.java │ │ │ │ ├── io │ │ │ │ ├── IOAccumulator.java │ │ │ │ ├── IOEvent.java │ │ │ │ ├── IOListener.java │ │ │ │ ├── IOMBean.java │ │ │ │ ├── IOMonitor.java │ │ │ │ └── IOProbe.java │ │ │ │ ├── network │ │ │ │ ├── AddressMatcher.java │ │ │ │ ├── DatagramEvent.java │ │ │ │ ├── DatagramIOAccumulator.java │ │ │ │ ├── DatagramIODetailAdder.java │ │ │ │ ├── DatagramIOMonitor.java │ │ │ │ ├── DatagramIOProbe.java │ │ │ │ ├── DatagramReceiveEvent.java │ │ │ │ ├── DatagramSendEvent.java │ │ │ │ ├── IoTraceSelectorProvider.java │ │ │ │ ├── MultiAddressMatcher.java │ │ │ │ ├── SocketCloseMonitor.java │ │ │ │ ├── SocketCloseMonitorMBean.java │ │ │ │ ├── SocketEvent.java │ │ │ │ ├── SocketIOAccumulator.java │ │ │ │ ├── SocketIODetailAdder.java │ │ │ │ ├── SocketIOMBean.java │ │ │ │ ├── SocketIOMonitor.java │ │ │ │ ├── SocketIOProbe.java │ │ │ │ ├── SocketReadEvent.java │ │ │ │ ├── SocketWriteEvent.java │ │ │ │ ├── SubnetAddress.java │ │ │ │ ├── UnclosedSocketProbe.java │ │ │ │ └── UnclosedStats.java │ │ │ │ ├── stack │ │ │ │ ├── FilterSequence.java │ │ │ │ ├── FrameMatcher.java │ │ │ │ ├── MinutiaFilter.java │ │ │ │ ├── RecursionFilter.java │ │ │ │ ├── Stack.java │ │ │ │ ├── StackFilter.java │ │ │ │ └── StandardFilter.java │ │ │ │ ├── stats │ │ │ │ ├── IODetailAdder.java │ │ │ │ ├── StackStatisticSource.java │ │ │ │ ├── StatisticAdder.java │ │ │ │ └── ValueReporter.java │ │ │ │ ├── thread │ │ │ │ ├── CPUStats.java │ │ │ │ ├── CpuUsageMBean.java │ │ │ │ ├── ProcessorProbe.java │ │ │ │ ├── ThreadDetails.java │ │ │ │ ├── ThreadFilter.java │ │ │ │ └── ThreadMonitor.java │ │ │ │ └── util │ │ │ │ ├── Configuration.java │ │ │ │ ├── DefineLaterPrintStream.java │ │ │ │ └── Output.java │ │ │ └── sun │ │ │ └── misc │ │ │ └── IoTrace.java │ └── test │ │ ├── java │ │ └── com │ │ │ └── dell │ │ │ └── gumshoe │ │ │ ├── TestIOAccumulator.java │ │ │ ├── TestSocketDiag.java │ │ │ ├── file │ │ │ └── TestPathMatcher.java │ │ │ ├── socket │ │ │ ├── TestParsing.java │ │ │ └── TestSubnetMatcher.java │ │ │ └── stack │ │ │ └── TestStackFilter.java │ │ └── resources │ │ └── gumshoe.properties └── unused │ └── IODetail.java ├── gumshoe-tools ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── dell │ │ │ └── gumshoe │ │ │ ├── inspector │ │ │ ├── FileDataParser.java │ │ │ ├── GUI.java │ │ │ ├── Main.java │ │ │ ├── ReportSource.java │ │ │ ├── Tool.java │ │ │ ├── graph │ │ │ │ ├── Box.java │ │ │ │ ├── DisplayOptions.java │ │ │ │ ├── Ruler.java │ │ │ │ ├── StackFrameNode.java │ │ │ │ └── StackGraphPanel.java │ │ │ ├── helper │ │ │ │ ├── CPUUsageHelper.java │ │ │ │ ├── DataTypeHelper.java │ │ │ │ ├── DatagramIOHelper.java │ │ │ │ ├── FileIOHelper.java │ │ │ │ ├── IOHelper.java │ │ │ │ ├── SocketIOHelper.java │ │ │ │ └── UnclosedSocketHelper.java │ │ │ └── tools │ │ │ │ ├── AboutPanel.java │ │ │ │ ├── DetailPanel.java │ │ │ │ ├── FilterEditor.java │ │ │ │ ├── HasCloseButton.java │ │ │ │ ├── OptionEditor.java │ │ │ │ ├── ProbeSourcePanel.java │ │ │ │ ├── ReportFileChooser.java │ │ │ │ ├── ReportSelectionListener.java │ │ │ │ └── StatisticChooser.java │ │ │ └── util │ │ │ └── Swing.java │ └── resources │ │ ├── about.png │ │ ├── detail.png │ │ ├── filters.png │ │ ├── graph.png │ │ ├── larger.png │ │ ├── next.png │ │ ├── open.png │ │ ├── prev.png │ │ ├── probe.png │ │ ├── resize.png │ │ ├── smaller.png │ │ └── stats.png │ └── test │ ├── java │ └── com │ │ └── dell │ │ └── gumshoe │ │ └── tools │ │ └── TestFileReader.java │ └── resources │ ├── .keep │ └── sample-data.txt └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .settings 2 | .project 3 | .classpath 4 | target 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | deploy: 3 | provider: releases 4 | api_key: 5 | secure: d/mfau/CalqxVJeGKuFzeHH1aQ+uQL3n1Jj/mGv5NqubkIqKA/G8Fn12NBsVQn+0l9gelEPa9ztfnOYIsbRyTXgR932x/bWrUkrjMG5bmjo60QESfrP9HUiXxWheAqNvuLhoK0s9KxQbKp4lBbRlHlLXMyPT4+Q4SC+4/FYLBWnukoqeur+RDRcJTdpCleEozK1k/T6Rx9hsJnnUcR61kLLxk/tg2GkbhvuASwCHaezaidcJgbsHNrJVwjMebxQyYhYOymPS1h3iFcVg7+WOXSXMnpo5LfYxpdkaYg4gng2NDF5DfGyiT9ssn26OWeYC+rv5DszjqoF1kDzRb7u0k0LIraicjaxSCRBTwq7+Pwzwjzj/5cKLE3nJ+BRw55VfRwBSV9PobmNxdW2C/jwkDpvtp7lk7s9Y+O12Jv+Xqip/iuDUC+oZ1ljykTG7e180/Un5cYeG6D10e69uMpNLmSNrfhg0zoBF07kVJfNmYMyzgik7PDOwBfbKrHafEsHcF5YSuunh2zwbl25J1s0y2AIW6KBvYZr5a4S5ZK0jp8HdqRVp4k5b6CpjhjZi40SZH2z2X1f0gerAezqorZrA42ZJZqQBQsirMpBvVVHZXl0yJw5oKS9pvbXa/0ZUKAKvHO5yt3oNpKYBl676Uo5+UHN1d/v4WEZoYuEcJ+bfjRc= 6 | file: 7 | - gumshoe-probes/target/gumshoe-agent.jar 8 | - gumshoe-tools/target/inspector.jar 9 | on: 10 | repo: dcm-oss/gumshoe 11 | tags: true 12 | all_branches: true 13 | overwrite: true 14 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright 2016 Dell Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Gumshoe Load Investigator 3 | ========================= 4 | 5 | Overview 6 | -------- 7 | 8 | Monitor application performance statistics associated with individual calling stacks, 9 | interactively filter and view as a flame graph or root graph. 10 | 11 | Gumshoe was first created initially for internal use in the Dell Cloud Manager application but 12 | source code has since been released for public use under [these terms](COPYRIGHT). 13 | 14 | [Features](docs/features.md) 15 | -------- 16 | 17 | * Analyze resource usage: measure TCP, UDP, filesystem or processor utilization 18 | * Pinpoint lines of code: all statistics are associated with a call stack and individual stack frames 19 | * Capture, filter and visualize as statistics are generated 20 | * Intuitive views: flame graph and root graph 21 | * Filter stack frames at capture and/or during visualization, modify on the fly. 22 | 23 | Documentation 24 | ------------- 25 | 26 | * Short intro and demo on youtube: [latest](https://www.youtube.com/watch?v=GGJFZfwXJ44) or the original [boring version](https://www.youtube.com/watch?v=1M9GX4ENMeI). 27 | * [Quick start guide](QUICK-START.md) walks through using with a sample application. 28 | * Full [user guide](docs/index.md) 29 | 30 | Don't Just Measure and Report: Understand 31 | ------------------------------------------ 32 | 33 | Looking at stack traces scaled by metrics gives a fast, intuitive way to understand application resource usage. Consider 34 | network I/O in a hypothetical application... 35 | 36 | Flame graph example: 37 | 38 | ![image](docs/flame-graph.png) 39 | 40 | Looking at callers into an application, Invoker.invoke is responsible for >90% of the read operations 41 | in this application. It always calls RestRequestHandler.handle which in turn calls three different REST 42 | operations. One -- showItemDetails -- is responsible for >80% of read operations as it makes different calls 43 | into the DAO layer. 44 | 45 | Root graph example: 46 | 47 | ![image](docs/root-graph.png) 48 | 49 | Grouping now by the last frame where the application calls out to a resource, 50 | the couchdb Database.getDocument is responsible for over half of the read operations, 51 | while the JDBC Statement.execute about 25%, the vast majority of those from getItemDetails. 52 | 53 | These two examples are completely contrived, but not overly simplified. The original call stacks in 54 | your application are generally huge, with hundreds of frames, 55 | but gumshoe uses [stack filters](docs/filters.md) to find 56 | just the relevant portions and focus the view on just those parts. 57 | -------------------------------------------------------------------------------- /bin/inspector: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | 4 | ## where is gumshoe installed? probably right here 5 | if [ -z "$GUMSHOE_HOME" ] 6 | then 7 | bindir="`dirname $0`" 8 | if [ . = "$bindir" ] 9 | then 10 | bindir="`pwd`" 11 | fi 12 | GUMSHOE_HOME="`dirname $bindir`" 13 | fi 14 | 15 | ## make sure jar is built 16 | jarfile="$GUMSHOE_HOME/gumshoe-tools/target/inspector.jar" 17 | if [ ! -f "$jarfile" ] 18 | then 19 | echo 'ERROR: gumshoe inspector executable jar not found (project not built?)' 1>&2 20 | echo 'First build with: mvn install' 1>&2 21 | exit 1 22 | fi 23 | 24 | ## default 5min reporting period for any probes enabled 25 | period=300000 # 5min 26 | 27 | ## add args for probes 28 | needagent=false 29 | needprobe=false 30 | while [ $# -gt 0 ] 31 | do 32 | case "$1" in 33 | -cpu) 34 | gumshoe_opts="$gumshoe_opts -Dgumshoe.cpu-usage.period=$period " 35 | gumshoe_opts="$gumshoe_opts -Dgumshoe.cpu-usage.sample=5000" 36 | gumshoe_opts="$gumshoe_opts -Dgumshoe.cpu-usage.filter.none=true" 37 | needprobe=true 38 | ;; 39 | -file-io) 40 | gumshoe_opts="$gumshoe_opts -Dgumshoe.file-io.period=$period" 41 | gumshoe_opts="$gumshoe_opts -Dgumshoe.file-io.filter.none=true" 42 | needagent=true 43 | needprobe=true 44 | ;; 45 | -socket-io) 46 | gumshoe_opts="$gumshoe_opts -Dgumshoe.socket-io.period=$period" 47 | gumshoe_opts="$gumshoe_opts -Dgumshoe.socket-io.filter.none=true" 48 | needagent=true 49 | needprobe=true 50 | ;; 51 | -datagram-io) 52 | gumshoe_opts="$gumshoe_opts -Dgumshoe.datagram-io.period=$period" 53 | gumshoe_opts="$gumshoe_opts -Dgumshoe.datagram-io.filter.none=true" 54 | needprobe=true 55 | ;; 56 | -unclosed) 57 | gumshoe_opts="$gumshoe_opts -Dgumshoe.socket-unclosed.period=$period" 58 | gumshoe_opts="$gumshoe_opts -Dgumshoe.socket-unclosed.filter.none=true" 59 | needprobe=true 60 | ;; 61 | -help) 62 | cat << HELPTEXT 63 | Inspector for gumshoe 64 | 65 | Usage: $0 [options...] [target-java-main [target-java-args...]] 66 | 67 | Options: 68 | -help Print this message 69 | -cpu Install cpu probe 70 | -file-io Install file I/O probe 71 | -socket-io Install file I/O probe 72 | -datagram-io Install datagram I/O probe 73 | -unclosed Install unclosed socket probe 74 | 75 | Any other options are passed to the JRE. 76 | 77 | HELPTEXT 78 | exit 79 | ;; 80 | -*) 81 | user_opts="$user_opts $1" 82 | ;; 83 | *) 84 | break 85 | ;; 86 | esac 87 | 88 | shift 89 | done 90 | 91 | # by default inspector doesn't usually watch itself, 92 | # so probes are only installed if there is a target. 93 | # override if probe was requested... 94 | if $needprobe && [ $# -eq 0 ] 95 | then 96 | gumshoe_opts="$gumshoe_opts -Dgumshoe.probe.enabled=true " 97 | fi 98 | 99 | if $needagent 100 | then 101 | agent="$GUMSHOE_HOME/gumshoe-probes/target/gumshoe-agent.jar" 102 | if [ ! -f "$agent" ] 103 | then 104 | echo ERROR: did not find gumshoe agent $agent 1>&2 105 | exit 1 106 | fi 107 | 108 | gumshoe_opts="$gumshoe_opts -javaagent:$agent" 109 | fi 110 | 111 | ## now check if any user options override 112 | 113 | # if user did not specify max heap, set default 114 | opts="$user_opts" 115 | echo " $user_opts" | grep -- ' -Xms' > /dev/null 116 | if [ $? -ne 0 ] 117 | then 118 | opts="-Xms1g $user_opts" 119 | fi 120 | 121 | # for each property, if user did not specify add ours 122 | for prop in $gumshoe_opts 123 | do 124 | regex=" `echo $prop | cut -d= -f1`=" 125 | echo " $user_opts" | grep -- "$regex" > /dev/null 126 | if [ $? -ne 0 ] 127 | then 128 | opts="$opts $prop" 129 | fi 130 | done 131 | 132 | ## warning: can fail if orig cmdline contains quoted expressions like: java some.MainClass "first arg" second "third arg" 133 | echo EXECUTING: java $opts -jar "$jarfile" $* 134 | java $opts -jar "$jarfile" $* 135 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | Gumshoe Features 2 | ================ 3 | 4 | Data Collection 5 | --------------- 6 | 7 | These kinds of resource usage are monitored and reported: 8 | - [socket I/O (TCP)](types/socket-io.md) 9 | - [file I/O](types/file-io.md) 10 | - [CPU utilization](types/cpu-stats.md) 11 | - [datagram I/O (UDP)](types/datagram-io.md) 12 | - [unclosed socket detection](types/socket-unclosed.md) 13 | 14 | Visualization 15 | ------------- 16 | 17 | All data collection is associated with a specific call stack, so information is not "system-wide" 18 | but tied to individual threads and method calls. If multiple threads include the same method call, 19 | resource usage can be combined across threads for a total for that method call over all similar threads. 20 | This can be presented by combining identical frames starting at the bottom of the call stack, 21 | which results in a flame graph; or starting at the bottom, which results in a root graph. 22 | 23 | Live capture and view 24 | --------------------- 25 | 26 | Often data is collected from the target application in a text file and analyzed later with the gumshoe viewer. 27 | However, the viewer can also be launched from within the same JVM and data viewed as it is collected. -------------------------------------------------------------------------------- /docs/flame-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/docs/flame-graph.png -------------------------------------------------------------------------------- /docs/hooks.md: -------------------------------------------------------------------------------- 1 | Gumshoe Hooks 2 | ============= 3 | 4 | Hooks are techniques used by gumshoe to collect information from the JVM that is filtered, accumulated and 5 | finally reported by probes. Some probes may share a hook, and some probes may work without a hook. 6 | 7 | List of hooks: 8 | 9 | [IoTrace](hooks/io-trace.md) is packaged in the agent jar and must be loaded 10 | into the JVM to support socket I/O and file I/O probes. 11 | 12 | [SelectorProviderImpl](hooks/selector-provider.md) requires a System property 13 | during startup to override the default factory to support NIO monitoring in 14 | the socket I/O and datagram I/O probes. 15 | 16 | [SocketFactoryImpl](hooks/socket-factory.md) is installed by the unclosed socket probe. 17 | 18 | [DatagramFactoryImpl](hooks/datagram-socket-factory.md) is installed by the datagram I/O probe. 19 | 20 | -------------------------------------------------------------------------------- /docs/hooks/datagram-socket-factory.md: -------------------------------------------------------------------------------- 1 | Datagram I/O Hook 2 | ================= 3 | 4 | Summary 5 | ------- 6 | 7 | The DatagramSocketFactoryImpl mechanism was used to define wrapper classes for regular (non-NIO) 8 | datagram sockets that report I/O statistics to IoTraceAdapter. The wrappers invoke the actual datagram operations 9 | on the original default factory implementation while tracking operations, size and elapsed time. 10 | -------------------------------------------------------------------------------- /docs/hooks/io-trace.md: -------------------------------------------------------------------------------- 1 | IoTrace Hook 2 | ============ 3 | 4 | Summary 5 | ------- 6 | 7 | To collect socket or file I/O statistics, the target JVM must include the option: 8 | 9 | -javaagent 10 | 11 | 12 | Details 13 | ------- 14 | 15 | At its core, gumshoe collects socket and file I/O data using a callback 16 | mechanism built into the JRE. 17 | Socket and file operations make a call to an empty class IoTrace before and after 18 | each read or write operation. The gumshoe hook replaces this empty class with one 19 | that can report the details of those operations and the call stack where it occurred. 20 | 21 | To override a built-in class from the JRE rt.jar file, the target application 22 | will need to start with a option shown above. 23 | Failure to include this option will not prevent your application from running, 24 | but gumshoe will be unable to collect data about socket or file I/O. 25 | Other kinds of statistics can still be collected. 26 | 27 | Having the hook in place will allow applications to start and stop monitoring 28 | as needed, and it will introduce almost no overhead when not monitoring. 29 | Specifically, instead of the default empty method in IoTrace, the replacement 30 | [IoTrace](../../gumshoe-hooks/src/main/java/sun/misc/IoTrace.java) 31 | methods make one call into a 32 | [null object](https://en.wikipedia.org/wiki/Null_Object_pattern) IoTraceAdapter 33 | 34 | The IoTraceAdapter used provides additional methods to collect datagram I/O 35 | using similar methods. The datagram hooks report directly to this adapter 36 | and not sun.misc.IoTrace, however, so the original class signature of 37 | IoTrace is unchanged. 38 | -------------------------------------------------------------------------------- /docs/hooks/selector-provider.md: -------------------------------------------------------------------------------- 1 | NIO Monitoring Hook 2 | =================== 3 | 4 | The NIO SelectorProviderImpl mechanism is used to define wrapper classes that add missing IoTrace callsback 5 | to NIO socket non-blocking reads and IoTraceAdapter callsbacks to datagram NIO operations. IO is passed 6 | to the original default implementation and the wrapper only provides event reporting. 7 | 8 | Without the hook, all NIO socket writes, and blocking NIO socket reads and all file I/O are still reported. 9 | The hook is needed for non-blocking socket reads and datagram I/O only. 10 | 11 | To use the hook, the JVM must start with system property: 12 | 13 | java.nio.channels.spi.SelectorProvider=com.dell.gumshoe.network.IoTraceSelectorProvider -------------------------------------------------------------------------------- /docs/hooks/socket-factory.md: -------------------------------------------------------------------------------- 1 | Unclosed Socket Hook 2 | ==================== 3 | 4 | Summary 5 | ------- 6 | 7 | The SocketFactoryImpl mechanism was used to define wrapper classes for regular (non-NIO) 8 | sockets that track closure and report sockets left open. I/O operations are passed to 9 | the original default factory generated sockets. -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | Gumshoe User Guide 2 | ================== 3 | 4 | Overview 5 | -------- 6 | 7 | Gumshoe intercepts java calls to sockets and captures network statistics. 8 | Data can be logged to a file or viewed live. 9 | 10 | [Features](features.md) 11 | ----------------------- 12 | 13 | - Network I/O analysis (TCP and UDP) 14 | - CPU utilization analysis 15 | - unclosed socket detection 16 | - file I/O analysis 17 | 18 | Getting Started 19 | --------------- 20 | 21 | - Examples 22 | - ELI5: Performance tuning 23 | - Components of gumshoe 24 | - Step by step 25 | 26 | 27 | Generating Reports 28 | ------------------ 29 | 30 | - [About the hooks](hooks.md) 31 | - [Configuring the probe](probe.md) 32 | - [Using filters](filters.md) 33 | - [Running your program](run.md) 34 | 35 | Viewing Reports 36 | --------------- 37 | 38 | - Running gumshoe GUI from your JVM 39 | - Running standalone GUI 40 | - Selecting a data report 41 | - Navigating the graph 42 | - Graph display options 43 | - Configuring filters 44 | 45 | Understanding Performance 46 | ------------------------- 47 | 48 | - Identifying bottlenecks 49 | - Stacks vs Statistics 50 | - Scalability -------------------------------------------------------------------------------- /docs/probe.md: -------------------------------------------------------------------------------- 1 | Gumshoe Probe 2 | ============= 3 | 4 | A gumshoe probe is the set of classes that receive, filter and forward information from the gumshoe hooks 5 | or the JDK itself. Unlike the hooks, the probe needs only to be in the classpath (but is generally included as part of the javaagent). 6 | 7 | Managing Data Collection 8 | ------------------------ 9 | 10 | The com.dell.gumshoe.ProbeManager class is a utility class that manages components that listen and forward 11 | gumshoe data and can be managed directly, using JMX or using Properties. 12 | 13 | The ProbeManager is used to initialize and begin monitoring: 14 | - Add a servlet context listener or other lifecycle callback mechanism to call ProbeManager from within your application 15 | - Explicitly invoke ProbeManager methods from your application 16 | 17 | It can stop monitoring or update configuration: 18 | - Explicitly invoke ProbeManager methods from your application 19 | - Invoke JMX operations 20 | 21 | Handling Data 22 | ------------- 23 | 24 | Collected data is forwarded to a Listener at configured intervals. 25 | The probe package includes listeners to write data to standard out or a text file. 26 | 27 | Configuration Properties 28 | ------------------------ 29 | 30 | System.properties can be used with Probe.initialize() or Probe.main(), 31 | or any Properties object can be used with Probe.initialize(Properties). 32 | The values will install JVM hooks, manage filters and data collection, 33 | and determine when and where results are reported. 34 | 35 | For details: 36 | - [Properties for socket I/O reporting](probe/properties-socket-io.md) 37 | - [Properties for CPU usage reporting](probe/properties-cpu-stats.md) 38 | - [Properties for unclosed socket reporting](probe/properties-unclosed-socket.md) 39 | 40 | Managing Configuration with JMX 41 | ------------------------------- 42 | 43 | Many of the settings can also to be managed at runtime using MBeans. 44 | MBeans will be installed as part of Probe.initialize() 45 | for any monitors enabled; or Properties can override this behavior 46 | and install an MBean even if the monitor is not enabled during startup. 47 | (This would allow you to later connect, enable the monitor, 48 | and collect data only during a specific period of time.) 49 | 50 | To work with MBeans, a JMX service must be enabled in the JVM. 51 | Use system properties such as these: 52 | -Dcom.sun.management.jmxremote.port=1234 53 | -Dcom.sun.management.jmxremote.local.only=false 54 | -Dcom.sun.management.jmxremote.authenticate=false 55 | -Dcom.sun.management.jmxremote.ssl=false 56 | and connect to your JVM using a JMX client such as jconsole. 57 | 58 | By default, the MBeans installed will have names beginning with "com.dell.gumshoe". 59 | 60 | For details: 61 | - [Socket I/O MBean](probe/jmx-socket-io.md) 62 | - [CPU Stats MBean](probe/jmx-cpu-stats.md) 63 | - [Unclosed Socket MBean](probe/jmx-unclosed-socket.md) 64 | -------------------------------------------------------------------------------- /docs/probe/event-handling.md: -------------------------------------------------------------------------------- 1 | I/O Probe Event Handler 2 | ======================= 3 | 4 | The socket, datagram and file I/O probes each create entries in a (separate) bounded queue 5 | for each read and write operation to offload statistics tabulation from the thread 6 | performing I/O. If the consumer of the thread is not able to keep up with the events 7 | being generated, the queue will reach capacity and events may be lost. 8 | 9 | Monitoring the Queue 10 | -------------------- 11 | 12 | If an event queue fills, a message is displayed on STDOUT such as: 13 | 14 | GUMSHOE: IO event queue for SocketIOMonitor is full 15 | 16 | If this message is not found in STDOUT, then no further investigation or configuration is needed. 17 | If it is seen, use JMX MBeans to determine how many events are being lost. 18 | 19 | SocketIOProbe, DatagramIOProbe and FileIOProbe each have corresponding MBeans with attributes and operations: 20 | 21 | QueueStatisticsEnabled set true to collect number of events dropped and average queue size 22 | QueueStats text description of average and max queue size, number of events and % dropped 23 | resetQueueStats() operation to reset counters to zero 24 | 25 | Also to enable collecting queue statistics during startup, System properties can be used: 26 | 27 | gumshoe.socket-io.handler.stats-enabled=true 28 | gumshoe.datagram-io.handler.stats-enabled=true 29 | gumshoe.file-io.handler.stats-enabled=true 30 | 31 | Using these controls, enable statistics collection and monitor the target application for some time. 32 | Then look at QueueStats to see what portion of events are dropped. 33 | 34 | Live with It? 35 | ------------- 36 | 37 | In most cases, dropping even a significant fraction of the events is not a problem. The events 38 | received should still be a representative sample of all I/O and although the total counts and number of bytes 39 | may not be accurate, the relative weight of each stack and frame should still be usable. 40 | 41 | Deal with It! 42 | ------------- 43 | 44 | If you decide you are dropping more events than you are comfortable with, there are a number of ways 45 | to improve reporting. 46 | 47 | - if there are only intermittent periods of dropped events, increasing queue size may be enough to 48 | let the consumer handle events in between peaks. Use properties to set the queue size (default is 500): 49 | 50 | gumshoe.socket-io.handler.queue-size=1000 51 | gumshoe.datagram-io.handler.queue-size=1000 52 | gumshoe.file-io.handler.queue-size=1000 53 | 54 | The queue will fill if events are produced (individual I/O operations) faster than they are consumed. 55 | Some possible reasons: 56 | 57 | - The target application is performing a lot of small network operations 58 | 59 | This could be an area to improve the target application. 60 | Lots of small operations are less efficient than fewer large operations. 61 | 62 | Or this could just be the nature of the expected application load, 63 | so increase the gumshoe event queue size and the handler thread priority to accommodate. 64 | 65 | - The JVM is CPU bound 66 | 67 | The event queue may back up if the target application is CPU bound. This could be 68 | an issue in the target application itself, and you may want to look at 69 | [processor utilization statistics](../types/cpu-stats.md) before socket I/O. 70 | 71 | Or it could be due to gumshoe stack filters. Each stack filter configured has to 72 | modify the event call stack on the same event handling thread. Complex filters 73 | (such as the recursion filter) or deep call stacks can result in more load than the 74 | thread can handle. Relax [filters](../filters.md) (at the expense of more memory use) 75 | or increase the number of threads or the event handler thread priority. 76 | -------------------------------------------------------------------------------- /docs/probe/filter-properties.md: -------------------------------------------------------------------------------- 1 | Properties for Stack Filters 2 | ============================ 3 | 4 | Note on Abbreviated Property Names 5 | ---------------------------------- 6 | 7 | Each probe can have its own stack filter and each can define them using similar property names. 8 | Property names in this section are shown with a beginning ellipsis, such as: "...filter.top" 9 | Any probe can use this property with its specific prefix. For example, the socket I/O probe 10 | would use the full property name "gumshoe.socket-io.filter.top" 11 | 12 | This section describes the individual filter settings that can be used for any probe. 13 | For example, socket I/O monitoring will use the properties that begin "gumshoe.socket-io.filter..." 14 | In this section we will define the various properties that can be used 15 | 16 | Configuration Properties 17 | ------------------------ 18 | 19 | Stacks should generally be [filtered](../filters.md) reduce overhead and simplify later analysis: 20 | 21 | ...filter.simplify Retain only portions of the stack frames. Allowed values are 22 | NO_LINE_NUMBERS, NO_METHOD, NO_INNER_CLASSES, NO_CLASSES, NONE 23 | Default is "NONE" (do not simplify frames) 24 | ...filter.recursion.threshold The recursion filter is only applied to stacks with 25 | more than this number of frames (after other filters). 26 | There is no default value, recursion is not applied unless set. 27 | ...filter.recursion.depth Find and exclude frames performing recursion. 28 | The value must be an integer length indicating the length 29 | of the longest sequence of repeated frames the filter will find. 30 | The default is "1", but is unused unless the threshold is set. 31 | ...filter.exclude-jdk Exclude frames from java built-in packages and gumshoe 32 | ...filter.include Include only these packages or classes (comma-separated) 33 | ...filter.exclude Exclude these packages or classes 34 | ...filter.top Number of frames at top of stack to retain 35 | ...filter.bottom Number of frames at bottom of stack to retain 36 | ...filter.allow-empty-stack If filters excluded all frames from a stack, 37 | the full unfiltered stack can be restored (if false), 38 | or the empty stack will be used and collect stats 39 | as an "other" category. 40 | ...filter.none If true, override other filter settings: no filtering is done. 41 | 42 | -------------------------------------------------------------------------------- /docs/probe/jmx-cpu-stats.md: -------------------------------------------------------------------------------- 1 | MBean Management of CPU Stats Reporting 2 | ======================================= 3 | 4 | By default if CPU statistics reporting is enabled, an MBean will be also be installed. 5 | Properties used to initialize gumshoe can change this behavior and either force or disable 6 | installation of the MBean or assign a specific name to the bean installed. 7 | 8 | This mbean will allow you to alter these attributes: 9 | 10 | Enabled Enable collection of CPU usage statistics (true or false) 11 | ReportingFrequency How often statistics are reported to configured listeners (milliseconds) 12 | ShutdownReportEnabled Enable the shutdown hook that sends the final report to configured listeners (true or false) 13 | DumpInterval How frequently to collect thread statistics (milliseconds) 14 | EffectiveInterval (read only) Gumshoe will try to collect statistics at DumpInterval, 15 | but it may adjust the collection rate dynamically if needed. This 16 | is the current collection frequency (milliseconds). 17 | AverageDumpTime How long the collection process takes, average over all collections performed. (milliseconds) 18 | ThreadPriority Collection is performed asynchronously on a thread with this priority (default Thread.MIN_PRIORITY) 19 | 20 | In addition these operations can be performed: 21 | 22 | getReport() Return a text report of the current contents of the collection buffer. 23 | This will likely represent a partial report if periodic reporting is enabled 24 | for the time since the start of the last reporting interval. 25 | reset() Clear the contents of the collection buffer. 26 | If periodic reporting is enabled, the next report sent to configured listeners 27 | will not contain a full report as this data will have been removed. -------------------------------------------------------------------------------- /docs/probe/jmx-file-io.md: -------------------------------------------------------------------------------- 1 | MBean Management of File I/O Reporting 2 | ====================================== 3 | 4 | By default if socket I/O reporting is enabled, an MBean will be also be installed. 5 | Properties used to initialize gumshoe can change this behavior and either force or disable 6 | installation of the MBean or assign a specific name to the bean installed. 7 | 8 | This mbean will allow you to alter these attributes: 9 | 10 | Enabled Enable collection of socket I/O statistics (true or false) 11 | ReportingFrequency How often statistics are reported to configured listeners (milliseconds) 12 | ShutdownReportEnabled Enable the shutdown hook that sends the final report to configured listeners (true or false) 13 | 14 | In addition these operations can be performed: 15 | 16 | getReport() Return a text report of the current contents of the collection buffer. 17 | This will likely represent a partial report if periodic reporting is enabled 18 | for the time since the start of the last reporting interval. 19 | reset() Clear the contents of the collection buffer. 20 | If periodic reporting is enabled, the next report sent to configured listeners 21 | will not contain a full report as this data will have been removed. -------------------------------------------------------------------------------- /docs/probe/jmx-socket-io.md: -------------------------------------------------------------------------------- 1 | MBean Management of Socket I/O Reporting 2 | ======================================== 3 | 4 | By default if socket I/O reporting is enabled, an MBean will be also be installed. 5 | Properties used to initialize gumshoe can change this behavior and either force or disable 6 | installation of the MBean or assign a specific name to the bean installed. 7 | 8 | This mbean will allow you to alter these attributes: 9 | 10 | Enabled Enable collection of socket I/O statistics (true or false) 11 | ReportingFrequency How often statistics are reported to configured listeners (milliseconds) 12 | ShutdownReportEnabled Enable the shutdown hook that sends the final report to configured listeners (true or false) 13 | 14 | In addition these operations can be performed: 15 | 16 | getReport() Return a text report of the current contents of the collection buffer. 17 | This will likely represent a partial report if periodic reporting is enabled 18 | for the time since the start of the last reporting interval. 19 | reset() Clear the contents of the collection buffer. 20 | If periodic reporting is enabled, the next report sent to configured listeners 21 | will not contain a full report as this data will have been removed. -------------------------------------------------------------------------------- /docs/probe/jmx-unclosed-socket.md: -------------------------------------------------------------------------------- 1 | MBean Management of Unclosed Socket Reporting 2 | ============================================= 3 | 4 | By default if unclosed socket reporting is enabled, an MBean will be also be installed. 5 | Properties used to initialize gumshoe can change this behavior and either force or disable 6 | installation of the MBean or assign a specific name to the bean installed. 7 | 8 | This mbean will allow you to alter these attributes: 9 | 10 | Enabled Enable collection of unclosed socket statistics (true or false) 11 | ClearClosedSocketsInterval Set the value N used to clear closed sockets. Each Nth socket opened will cause 12 | the system to check all tracked sockets and see if any have closed and can be dropped. 13 | ReportingFrequency How often statistics are reported to configured listeners (milliseconds) 14 | ShutdownReportEnabled Enable the shutdown hook that sends the final report to configured listeners (true or false) 15 | 16 | In addition this operation can be performed: 17 | 18 | getReport(age) Return a text report of sockets currently open and having at least the given age (milliseconds). 19 | -------------------------------------------------------------------------------- /docs/probe/properties-cpu-stats.md: -------------------------------------------------------------------------------- 1 | Properties for CPU Stats Reporting 2 | ================================== 3 | 4 | These properties are used by the ProbeManager to install the probe, filter and forward CPU usage statistics collected. 5 | 6 | Configuration Properties 7 | ------------------------ 8 | 9 | Initialization can use system properties by calling ProbeManager.initialize() or with an explicit Properties argument. 10 | 11 | gumshoe.cpu-usage.period Reports will be generated at regular intervals (in milliseconds) 12 | gumshoe.cpu-usage.onshutdown If true, a report will be generated when the JVM exits 13 | gumshoe.cpu-usage.mbean If true, enable JMX control of CPU usage probe 14 | gumshoe.cpu-usage.mbean.name Override name of JMX control 15 | (default is based on fully qualified class name) 16 | gumshoe.cpu-usage.enabled By default, the usage probe is initialized if periodic or shutdown 17 | reporting is enabled. Override this behavior to install the probe 18 | now but enable/disable the reporting at another time. 19 | gumshoe.cpu-usage.priority Thread priority for data collection thread (default value is Thread.MIN_PRIORITY) 20 | gumshoe.cpu-usage.sample Thread data collection rate (milliseconds, default 5000) 21 | Generally multiple samples are accumulated into each report. 22 | gumshoe.cpu-usage.jitter Collection rate should vary randomly by this 23 | amount (milliseconds, default 0) 24 | gumshoe.cpu-usage.use-wait-times Thread contention monitoring is enabled by default on 25 | JVMs that support it. If false, contention monitoring 26 | is disabled. This may reduce overhead, but thread blocked and 27 | waiting times will not be reported. (Default is true.) 28 | 29 | Stacks should generally be [filtered](../filters.md) reduce overhead and simplify later analysis: 30 | 31 | gumshoe.cpu-usage.filter... See common filter properties [here](filter-properties.md) 32 | 33 | Collected data reports are written to: 34 | 35 | gumshoe.cpu-usage.output=none Do not write reports (ie, when your program is 36 | adding its own explicit Listener to receive reports) 37 | gumshoe.cpu-usage.output=stdout Write to System.out (the default) 38 | gumshoe.cpu-usage.output=file:/some/path Write to a text file. 39 | -------------------------------------------------------------------------------- /docs/probe/properties-datagram-io.md: -------------------------------------------------------------------------------- 1 | Properties for Datagram (UDP) I/O Monitoring 2 | ==================================== 3 | 4 | These properties are used by the ProbeManager to install the hook, filter and forward datagram I/O information generated. 5 | 6 | Configuration Properties 7 | ------------------------ 8 | 9 | Initialization can use system properties by calling ProbeManager.initialize() or with an explicit Properties argument. 10 | 11 | gumshoe.datagram-io.period Data reports will be generated at regular intervals (in milliseconds) 12 | gumshoe.datagram-io.onshutdown If true, a report will be generated when the JVM exits 13 | gumshoe.datagram-io.mbean If true, enable JMX control of datagram usage probe 14 | gumshoe.datagram-io.mbean.name Override name of JMX control 15 | (default is based on fully qualified class name) 16 | gumshoe.datagram-io.enabled By default, the usage probe is initialized if periodic or shutdown 17 | reporting is enabled. Override this behavior to install the probe 18 | now but enable/disable the reporting at another time. 19 | 20 | Collection may be limited to certain networks, systems or ports: 21 | 22 | gumshoe.datagram-io.include datagram endpoints to monitor 23 | gumshoe.datagram-io.exclude datagram endpoints not to monitor 24 | 25 | Both of these properties support comma-separated list of: 26 | : / 27 | for example: 28 | 192.168.3.0/24:80,127.0.0.1/32:* 29 | 30 | Statistics for a datagram are collected if the datagram endpoint: 31 | - matches "include" or "include" is blank 32 | - does not match "exclude" or "exclude" is blank 33 | 34 | Stacks should generally be [filtered](../filters.md) reduce overhead and simplify later analysis: 35 | 36 | gumshoe.datagram-io.filter... See common filter properties [here](filter-properties.md) 37 | 38 | Collected data reports are written to: 39 | 40 | gumshoe.datagram-io.output=none Do not write reports (ie, when your program is 41 | adding its own explicit Listener to receive reports) 42 | gumshoe.datagram-io.output=stdout Write to System.out (the default) 43 | gumshoe.datagram-io.output=file:/some/path Write to a text file. 44 | 45 | The event queue and consumer can be monitored and managed using: 46 | 47 | gumshoe.datagram-io.handler.stats-enabled Should statistics be collected on event queue size 48 | and number of events dropped due to a full 49 | queue (default is false) 50 | gumshoe.datagram-io.handler.queue-size Number of events that can be queued (default is 500) 51 | gumshoe.datagram-io.handler.count Number of consumer threads (default 1) 52 | gumshoe.datagram-io.handler.priority Thread priority (default 1=Thread.MIN_PRIORITY) 53 | 54 | See [event queue documentation](event-handling.md) for more information. 55 | -------------------------------------------------------------------------------- /docs/probe/properties-file-io.md: -------------------------------------------------------------------------------- 1 | Properties for File I/O Monitoring 2 | ================================== 3 | 4 | These properties are used by the ProbeManager to install the hook, filter and forward file I/O statistics generated. 5 | 6 | Configuration Properties 7 | ------------------------ 8 | 9 | Initialization can use system properties by calling ProbeManager.initialize() or with an explicit Properties argument. 10 | 11 | gumshoe.file-io.period Data reports will be reported at regular intervals (in milliseconds) 12 | gumshoe.file-io.onshutdown If true, data reports will be reported when the JVM exits 13 | gumshoe.file-io.mbean If true, enable JMX control of file usage probe 14 | gumshoe.file-io.mbean.name Override name of JMX control 15 | (default is based on fully qualified class name) 16 | gumshoe.file-io.enabled By default, the usage probe is initialized if periodic or shutdown 17 | reporting is enabled. Override this behavior to install the probe 18 | now but enable/disable the reporting at another time. 19 | 20 | Collection may be limited to certain file or directories: 21 | 22 | gumshoe.file-io.include Paths to monitor 23 | gumshoe.file-io.exclude Paths not to monitor 24 | 25 | Both of these properties support comma-separated list of wildcard expressions. 26 | For example: 27 | /tmp/**, *.dat, **/bob/** 28 | 29 | Statistics are collected if the file path: 30 | - does not matches "exclude" or "exclude" is blank 31 | - matches "include" 32 | 33 | Stacks should generally be [filtered](../filters.md) reduce overhead and simplify later analysis: 34 | 35 | gumshoe.file-io.filter... See common filter properties [here](filter-properties.md) 36 | 37 | 38 | Collected data reports are written to: 39 | 40 | gumshoe.file-io.output=none Do not write reports (ie, when your program is 41 | adding its own explicit Listener to receive reports) 42 | gumshoe.file-io.output=stdout Write to System.out (the default) 43 | gumshoe.file-io.output=file:/some/path Write to a text file. 44 | -------------------------------------------------------------------------------- /docs/probe/properties-socket-io.md: -------------------------------------------------------------------------------- 1 | Properties for Socket I/O Monitoring 2 | ==================================== 3 | 4 | These properties are used by the ProbeManager to install the hook, filter and forward socket I/O information generated. 5 | 6 | Configuration Properties 7 | ------------------------ 8 | 9 | Initialization can use system properties by calling ProbeManager.initialize() or with an explicit Properties argument. 10 | 11 | gumshoe.socket-io.period Data reports will be generated at regular intervals (in milliseconds) 12 | gumshoe.socket-io.onshutdown If true, a report will be generated when the JVM exits 13 | gumshoe.socket-io.mbean If true, enable JMX control of socket usage probe 14 | gumshoe.socket-io.mbean.name Override name of JMX control 15 | (default is based on fully qualified class name) 16 | gumshoe.socket-io.enabled By default, the usage probe is initialized if periodic or shutdown 17 | reporting is enabled. Override this behavior to install the probe 18 | now but enable/disable the reporting at another time. 19 | 20 | Collection may be limited to certain networks, systems or ports: 21 | 22 | gumshoe.socket-io.include Socket endpoints to monitor 23 | gumshoe.socket-io.exclude Socket endpoints not to monitor 24 | 25 | Both of these properties support comma-separated list of: 26 | : / 27 | for example: 28 | 192.168.3.0/24:80,127.0.0.1/32:* 29 | 30 | Statistics for a socket are collected if the socket endpoint: 31 | - matches "include" or "include" is blank 32 | - does not match "exclude" or "exclude" is blank 33 | 34 | Stacks should generally be [filtered](../filters.md) reduce overhead and simplify later analysis: 35 | 36 | gumshoe.socket-io.filter... See common filter properties [here](filter-properties.md) 37 | 38 | Collected data reports are written to: 39 | 40 | gumshoe.socket-io.output=none Do not write reports (ie, when your program is 41 | adding its own explicit Listener to receive reports) 42 | gumshoe.socket-io.output=stdout Write to System.out (the default) 43 | gumshoe.socket-io.output=file:/some/path Write to a text file. 44 | -------------------------------------------------------------------------------- /docs/probe/properties-unclosed-socket.md: -------------------------------------------------------------------------------- 1 | Properties for Unclosed Socket Reporting 2 | ======================================== 3 | 4 | These properties are used by the ProbeManager 5 | to install the hook, filter stacks, and forward information generated about unclosed sockets. 6 | 7 | Configuration Properties 8 | ------------------------ 9 | 10 | Initialization can use system properties by calling ProbeManager.initialize() or with an explicit Properties argument. 11 | 12 | gumshoe.socket-unclosed.period Reports will be generated at regular intervals (in milliseconds) 13 | gumshoe.socket-unclosed.onshutdown If true, a report will be generated when the JVM exits 14 | gumshoe.socket-unclosed.mbean If true, enable JMX control of socket usage probe 15 | gumshoe.socket-unclosed.mbean.name Override name of JMX control 16 | (default is based on fully qualified class name) 17 | gumshoe.socket-unclosed.enabled By default, the usage probe is initialized if periodic or shutdown 18 | reporting is enabled. Override this behavior to install the probe 19 | now but enable/disable the reporting at another time. 20 | 21 | Stacks should generally be [filtered](../filters.md) reduce overhead and simplify later analysis: 22 | 23 | gumshoe.socket-unclosed.filter... See common filter properties [here](filter-properties.md) 24 | 25 | Collected data reports are written to: 26 | 27 | gumshoe.socket-unclosed.output=none Do not write reports (ie, when your program is 28 | adding its own explicit Listener to receive reports) 29 | gumshoe.socket-unclosed.output=stdout Write to System.out (the default) 30 | gumshoe.socket-unclosed.output=file:/some/path Write to a text file. 31 | -------------------------------------------------------------------------------- /docs/root-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/docs/root-graph.png -------------------------------------------------------------------------------- /docs/run.md: -------------------------------------------------------------------------------- 1 | Running your Program 2 | ==================== 3 | 4 | Direct Invocation 5 | ----------------- 6 | 7 | Suppose your application starts with a command such as: 8 | 9 | java -classpath dist/JavaChat.jar:lib/* javachat.JavaChat --server --port 1234 10 | 11 | or more generally: 12 | 13 | java -classpath 14 | 15 | Then you'll want to make these changes to the command: 16 | 17 | - add -javaagent to install our hooks 18 | - add -Dproperties to configure the probes 19 | 20 | For example, to use default filters and report every 5min to STDOUT: 21 | 22 | java -javaagent:$LIB/gumshoe-agent.jar \ 23 | -classpath dist/JavaChat.jar:lib/* \ 24 | -Dgumshoe.socket-io.period=300000 \ 25 | javachat.JavaChat --server --port 1234 26 | 27 | Custom Integration 28 | ------------------ 29 | 30 | With container or some project designs it isn't practical to modify the main class, 31 | so a lifecycle listener, JMX trigger or some other mechanism might be needed in your code 32 | to start the probe. 33 | 34 | Add a snippet like this: 35 | 36 | ProbeManager mgr = new ProbeManager(); 37 | mgr.initialize(); // use System.properties() 38 | 39 | Or to avoid System.properties(): 40 | 41 | Properties p = new Properties(); 42 | p.load( someReader ); 43 | ProbeManager mgr = new ProbeManager(); 44 | mgr.initialize( p ); 45 | -------------------------------------------------------------------------------- /docs/types/cpu-stats.md: -------------------------------------------------------------------------------- 1 | Processor Utilization Data 2 | ========================== 3 | 4 | Data 5 | ---- 6 | 7 | Periodic thread dumps are collected along with CPU statistics per thread. 8 | Values reported include a count of threads performing each call stack per thread state (RUNNABLE, BLOCKED, WAITING, etc), accumulated user and total CPU time, count and time spent in blocked and waiting states. 9 | 10 | Hooks 11 | ----- 12 | 13 | Information is provided by the JVM's ThreadMXBean polled at periodic intervals. 14 | 15 | Limitations 16 | ----------- 17 | 18 | - Thread counts are cumulative over multiple samples. So a single thread alternating between 19 | RUNNABLE and WAITING state sampled 5 times may be reported as 2 RUNNABLE threads and 3 WAITING. 20 | 21 | - Per-thread CPU times and blocked/waiting times and counts 22 | reflect the totals for the thread, not necessarily the current call stack. 23 | 24 | Suppose you have an ExecutorService that is used to run two types of operations. 25 | One is CPU intensive but short, no blocking or waiting, 26 | The other has a long wait for some event, then returns. 27 | 28 | One thread may perform a mix of both operations, but a thread dump at a point in time 29 | will show just the one currently running. 30 | 31 | Total CPU time, number of wait events, etc. at the time of the thread dump would be a total 32 | accumulated over execution of several operations of both types, not values associated with just the 33 | specific stack trace from the one operation currently running. 34 | 35 | - Short-running infrequent tasks may completely escape collection. Generally this is not an issue, 36 | as the statistics are intended to find the most use call stacks. 37 | Use a java profiler (with significantly more overhead) if every method call is required. 38 | 39 | Certain situations could make this more likely. If you collect thread dumps every minute, and 40 | the target application has a resource-intensive task performed every 5min, the schedules could align 41 | to miss ever reporting the task (or could align to catch it at the same point each time and over-represent 42 | its load). In this case, vary the collection rate used. 43 | 44 | - Not all JDKs expose blocking and waiting counts and times, so these values may not always be 45 | available. The ThreadMXBean reports this ability with "isThreadContentionMonitoringSupported". 46 | 47 | - In large applications, this probe can result in high CPU and memory overhead. 48 | Performing thread dumps less frequently will reduce CPU usage. 49 | If memory is an issue, reduce stack size using coarser filters resulting in fewer frames. 50 | -------------------------------------------------------------------------------- /docs/types/datagram-io.md: -------------------------------------------------------------------------------- 1 | Datagram I/O Data 2 | ================= 3 | 4 | Data 5 | ---- 6 | 7 | For each unique call stack that performs sends or receives a datagram, 8 | gumshoe reports totals for read, write and combined: 9 | number of calls, number of bytes, and elapsed time. 10 | 11 | Samples collected can be filtered by IP, mask and port to limit reports to only certain systems or services. 12 | 13 | Hooks 14 | ----- 15 | 16 | Gumshoe uses a [DatagramSocketImplFactory](../hooks/datagram-socket-factory.md) 17 | to report regular (non-NIO) datagram I/O using an extension similar to IoTrace. 18 | 19 | For NIO operations, a custom [SelectorProvider hook](../hooks/selector-provider.md) 20 | wraps default implementations to report I/O. 21 | 22 | Datagram operations are reported as events which are queued and handled off the I/O thread. 23 | The [event handler](../probe/event-handling.md) accumulates these events 24 | and reports a total per calling stack. 25 | 26 | Limitations 27 | ----------- 28 | 29 | - NIO non-blocking operations are asynchronous, so the time reported is really the time to pass 30 | the data into a buffer, not the actual I/O time. The value is real and usable, representing 31 | how long the target application waited trying to perform I/O using the NIO library, but 32 | is not the same as TCP transmission time included in the non-NIO values for these statistics 33 | 34 | - The event queue between IoTrace and the statistics accumulator can back up if I/O events are 35 | happening faster than the accumulator can process them. This can result in two issues: 36 | 37 | - Statistics totals may lag the times reported. For example, a summary of I/O reported at 12:00 38 | may actually only include events up to 11:59. 39 | - While the queue capacity is exceeded, events may be dropped and a message is shown in STDOUT 40 | 41 | If this is happening occasionally during peak loads, it may not be an issue. I/O statistics are still 42 | gathered -- it just samples fewer operations when the queue is full. If this is happening a lot during the loads 43 | being tested then look at then [event handler configuration](../probe/event-handling.md). 44 | -------------------------------------------------------------------------------- /docs/types/file-io.md: -------------------------------------------------------------------------------- 1 | File I/O Data 2 | ============= 3 | 4 | Data 5 | ---- 6 | 7 | For each unique call stack that reads or writes file data, 8 | gumshoe reports totals for read, write and combined: 9 | number of calls, number of bytes, and elapsed time. 10 | 11 | Samples collected can be filtered with wildcards to limit reports to certain file names, directories or filesystems. 12 | 13 | Hooks 14 | ----- 15 | 16 | File I/O collection relies on a callback mechanism defined in [IoTrace](../hooks/io-trace.md). 17 | To use this mechanism the gumshoe hook must override the JRE version of IoTrace 18 | using the gumshoe agent. 19 | 20 | Operations are reported as events which are queued and handled off the I/O thread. 21 | The [event handler](../probe/event-handling.md) accumulates these events 22 | and reports a total per calling stack. 23 | 24 | Limitations 25 | ----------- 26 | 27 | - NIO non-blocking operations are asynchronous, so the time reported is really the time to pass 28 | the data into a buffer, not the actual read or write time. The value is real and usable, representing 29 | how long the target application waited trying to perform I/O using the NIO library, but 30 | is not the same as hardware read/write time shown in the non-NIO values for these statistics 31 | 32 | - The event queue between IoTrace and the statistics accumulator can back up if I/O events are 33 | happening faster than the accumulator can process them. This can result in two issues: 34 | 35 | - Statistics totals may lag the times reported. For example, a summary of I/O reported at 12:00 36 | may actually only include events up to 11:59. 37 | - While the queue capacity is exceeded, events may be dropped and a message is shown in STDOUT 38 | 39 | If this is happening occasionally during peak loads, it may not be an issue. I/O statistics are still 40 | gathered -- report just contain samples fewer when the queue is full. If this is happening a lot during the loads 41 | being tested then look at then [event handler configuration](../probe/event-handling.md). 42 | 43 | -------------------------------------------------------------------------------- /docs/types/socket-io.md: -------------------------------------------------------------------------------- 1 | Socket I/O Data 2 | =============== 3 | 4 | Data 5 | ---- 6 | 7 | For each unique call stack that performs I/O, gumshoe reports totals for read, write and combined: 8 | number of calls, number of bytes, and elapsed time. 9 | 10 | Samples collected can be filtered by IP, mask and port to limit reports to only certain systems or services. 11 | 12 | Hooks 13 | ----- 14 | 15 | Socket I/O collection relies on [IoTrace](../hooks/io-trace.md) 16 | which provides a callback mechanism 17 | for all I/O on normal (non-NIO) sockets, all NIO writes and NIO reads when in blocking mode. 18 | To use this mechanism the gumshoe hook must override the JRE version of IoTrace 19 | using the gumshoe agent. 20 | 21 | For NIO reads in non-blocking mode, a custom [SelectorProvider](../hooks/selector-provider.md) 22 | wraps default implementations and includes these reads using the normal IoTrace mechanism. 23 | 24 | The callback to IoTrace creates an event which is queued and handled off the I/O thread. The 25 | [event handler](../probe/event-handling.md) accumulates these events and reports a total per calling stack. 26 | 27 | Limitations 28 | ----------- 29 | 30 | - NIO non-blocking operations are asynchronous, so the time reported is really the time to pass 31 | the data into a buffer, not the actual I/O time. The value is real and usable, representing 32 | how long the target application waited trying to perform I/O using the NIO library, but 33 | is not the same as TCP transmission time included in the non-NIO values for these statistics 34 | 35 | - The event queue between IoTrace and the statistics accumulator can back up if I/O events are 36 | happening faster than the accumulator can process them. This can result in two issues: 37 | 38 | - Statistics totals may lag the times reported. For example, a summary of I/O reported at 12:00 39 | may actually only include events up to 11:59. 40 | - While the queue capacity is exceeded, events may be dropped and a message is shown in STDOUT 41 | 42 | If this is happening occasionally during peak loads, it may not be an issue. I/O statistics are still 43 | gathered -- reports just contain fewer samples when the queue is full. If this is happening a lot during the loads 44 | being tested then look at then [event handler configuration](../probe/event-handling.md). 45 | -------------------------------------------------------------------------------- /docs/types/socket-unclosed.md: -------------------------------------------------------------------------------- 1 | Unclosed Socket Monitor 2 | ======================= 3 | 4 | Data 5 | ---- 6 | 7 | All open sockets are tracked with the original calls stack that opened them. Number of sockets per unique call 8 | stack is reported along with the max length a socket has been open. 9 | 10 | Hooks 11 | ----- 12 | 13 | A [custom SocketFactoryImpl](../hooks/socket-factory.md) wraps each regular (non-NIO) socket created 14 | to track the original call stack and creation time. 15 | 16 | Limitations 17 | ----------- 18 | 19 | NIO sockets are not currently tracked. -------------------------------------------------------------------------------- /gumshoe-probes/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /gumshoe-probes/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | gumshoe-probes 6 | 7 | runtime probes for gumshoe load analysis 8 | 9 | 4.12 10 | 11 | 12 | 13 | 14 | junit 15 | junit 16 | ${junit.version} 17 | test 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | com.coderplus.maven.plugins 27 | copy-rename-maven-plugin 28 | 1.0.1 29 | 30 | 31 | rename-file 32 | compile 33 | 34 | rename 35 | 36 | 37 | ${project.build.outputDirectory}/sun/misc/IoTrace.class 38 | ${project.build.outputDirectory}/sun.misc.IoTrace.class 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-compiler-plugin 46 | 3.3 47 | 48 | 1.7 49 | 1.7 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-surefire-plugin 55 | 56 | true 57 | 58 | **/*Test*.* 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-assembly-plugin 65 | 66 | 67 | 68 | attached 69 | 70 | package 71 | 72 | 73 | jar-with-dependencies 74 | 75 | 76 | 77 | com.dell.gumshoe.Agent 78 | true 79 | gumshoe-agent.jar 80 | 81 | 82 | gumshoe-agent 83 | false 84 | 85 | 86 | 87 | target/classes/sun/misc/IoTrace.class 88 | sun.misc.IoTrace.class 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | com.dell 99 | 0.1.0-SNAPSHOT 100 | Gumshoe Probes 101 | 102 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/Agent.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe; 2 | 3 | import sun.misc.IoTrace; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.lang.instrument.ClassDefinition; 12 | import java.lang.instrument.Instrumentation; 13 | import java.util.jar.JarEntry; 14 | import java.util.jar.JarInputStream; 15 | 16 | /** install our version of IoTrace to capture socket and file I/O activity 17 | */ 18 | public class Agent { 19 | private static final String CLASS_FILENAME = "/sun.misc.IoTrace.class"; 20 | private static boolean WAS_INSTALLED = false; 21 | 22 | public static void premain(String args, Instrumentation inst) throws Exception { 23 | final ProbeManager probeManager = ProbeManager.getInstance(); 24 | probeManager.initialize(); 25 | if(probeManager.isUsingIoTrace()) { 26 | new Agent().installIoTraceHook(inst); 27 | } 28 | WAS_INSTALLED = true; 29 | } 30 | 31 | public static boolean isAgentInstalled() { 32 | return WAS_INSTALLED; 33 | } 34 | 35 | private void installIoTraceHook(Instrumentation inst) throws IOException { 36 | final byte[] alternate = getAlternateBytecode(); 37 | if(alternate==null) { 38 | System.out.println("GUMSHOE ERROR: failed to locate IoTrace hook"); 39 | return; 40 | } 41 | try { 42 | inst.redefineClasses(new ClassDefinition(IoTrace.class, alternate)); 43 | if(Boolean.getBoolean("gumshoe.verbose")) { 44 | System.out.println("GUMSHOE: installed IoTrace hook"); 45 | } 46 | } catch(Exception e) { 47 | System.out.println("GUMSHOE ERROR: failed to install IoTrace hook"); 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | /** loop over classpath */ 53 | private byte[] getAlternateBytecode() throws IOException { 54 | final InputStream in = ProbeManager.class.getResourceAsStream(CLASS_FILENAME); 55 | final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 56 | final byte[] block = new byte[8192]; 57 | int len; 58 | while((len=in.read(block))>-1) { 59 | buffer.write(block, 0, len); 60 | } 61 | return buffer.toByteArray(); 62 | } 63 | 64 | private static byte[] getAlternateBytecodeOLD() throws IOException { 65 | final String[] classpath = System.getProperty("java.class.path").split(System.getProperty("path.separator")); 66 | for(String entry : classpath) { 67 | final File file = new File(entry); 68 | if( ! file.canRead()) continue; 69 | 70 | if(file.isFile()) { 71 | final JarInputStream in = new JarInputStream(new FileInputStream(file)); 72 | try { 73 | final byte[] contents = getFromJar(in); 74 | if(contents!=null) { return contents; } 75 | } finally { 76 | in.close(); 77 | } 78 | } else { 79 | // this case happens during development 80 | if(file.isDirectory()) { 81 | final File classFile = new File(file, CLASS_FILENAME); 82 | if(classFile.isFile()) { 83 | return getFromFile(classFile); 84 | } 85 | } 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | private static byte[] getFromJar(JarInputStream jarIn) throws IOException { 92 | JarEntry entry; 93 | while((entry = jarIn.getNextJarEntry())!=null) { 94 | if(CLASS_FILENAME.equals(entry.getName())) { 95 | return getFromStream(jarIn, entry.getSize()); 96 | } 97 | } 98 | return null; 99 | } 100 | 101 | private static byte[] getFromFile(File file) throws IOException { 102 | final InputStream in = new BufferedInputStream(new FileInputStream(file)); 103 | try { 104 | return getFromStream(in, file.length()); 105 | } finally { 106 | in.close(); 107 | } 108 | } 109 | 110 | private static byte[] getFromStream(InputStream in, long size) throws IOException { 111 | final byte[] contents = new byte[(int)size]; 112 | int pos = 0; 113 | int len; 114 | while(size-pos>0 && (len=in.read(contents, pos, (int)size-pos))!=-1) { 115 | pos+=len; 116 | } 117 | return contents; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/file/FileIOAccumulator.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.file; 2 | 3 | import com.dell.gumshoe.io.IOAccumulator; 4 | import com.dell.gumshoe.stack.StackFilter; 5 | 6 | /** create hierarchical analysis of socket IO versus stack 7 | * with total IO at each frame and 8 | * link from frame to multiple next frames below 9 | */ 10 | public class FileIOAccumulator extends IOAccumulator { 11 | 12 | public FileIOAccumulator(StackFilter filter) { 13 | super(filter); 14 | } 15 | 16 | @Override 17 | public FileIODetailAdder newAdder() { 18 | return new FileIODetailAdder(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/file/FileIODetailAdder.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.file; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.io.IOEvent; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StatisticAdder; 7 | 8 | public class FileIODetailAdder extends IODetailAdder { 9 | @Override 10 | public String getType() { 11 | return ProbeManager.FILE_IO_LABEL; 12 | } 13 | 14 | @Override 15 | public StatisticAdder newInstance() { 16 | return new FileIODetailAdder(); 17 | } 18 | 19 | public static FileIODetailAdder fromString(String line) { 20 | final FileIODetailAdder out = new FileIODetailAdder(); 21 | try { 22 | out.setFromString(line); 23 | } catch(Exception e) { 24 | return null; 25 | } 26 | return out; 27 | } 28 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/file/FileIOEvent.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.file; 2 | 3 | import com.dell.gumshoe.io.IOEvent; 4 | 5 | public abstract class FileIOEvent extends IOEvent { 6 | public FileIOEvent(String path) { 7 | setTarget(path); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/file/FileIOMonitor.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.file; 2 | 3 | import com.dell.gumshoe.hook.IoTraceListener.FileListener; 4 | import com.dell.gumshoe.io.IOMonitor; 5 | 6 | /** monitor socket IO and report each as an event to registered listeners 7 | */ 8 | public class FileIOMonitor extends IOMonitor implements FileListener { 9 | private final FileMatcher fileFilter; 10 | 11 | public FileIOMonitor() { 12 | this(FileMatcher.ANY, 500, Thread.MIN_PRIORITY, 1, false); 13 | } 14 | 15 | public FileIOMonitor(FileMatcher fileFilter, int queueSize, int priority, int count, boolean statsEnabled) { 16 | this.fileFilter = fileFilter; 17 | setEventQueueSize(queueSize); 18 | setThreadCount(count); 19 | setThreadPriority(priority); 20 | setQueueStatisticsEnabled(statsEnabled); 21 | } 22 | 23 | @Override 24 | public Object fileReadBegin(String path) { 25 | return fileFilter.matches(path) ? FileReadEvent.begin(path) : null; 26 | } 27 | 28 | @Override 29 | public Object fileWriteBegin(String path) { 30 | return fileFilter.matches(path) ? FileWriteEvent.begin(path) : null; 31 | } 32 | 33 | @Override 34 | public void fileReadEnd(Object context, long bytesRead) { 35 | handleEvent(context, bytesRead); 36 | } 37 | 38 | @Override 39 | public void fileWriteEnd(Object context, long bytesWritten) { 40 | handleEvent(context, bytesWritten); 41 | } 42 | 43 | private void handleEvent(Object context, long bytes) { 44 | if(context!=null) { 45 | final FileIOEvent operation = (FileIOEvent)context; 46 | operation.complete(bytes); 47 | queueEvent(operation); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/file/FileIOProbe.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.file; 2 | 3 | import com.dell.gumshoe.io.IOAccumulator; 4 | import com.dell.gumshoe.io.IOMonitor; 5 | import com.dell.gumshoe.io.IOProbe; 6 | import com.dell.gumshoe.stack.StackFilter; 7 | import com.dell.gumshoe.util.Configuration; 8 | 9 | import java.text.ParseException; 10 | 11 | public class FileIOProbe extends IOProbe { 12 | public static final String LABEL = "file-io"; 13 | 14 | public FileIOProbe(ProbeServices services) { 15 | super(services); 16 | } 17 | 18 | @Override 19 | public String getLabel() { 20 | return LABEL; 21 | } 22 | 23 | @Override 24 | protected IOMonitor createMonitor(Configuration p) throws ParseException { 25 | final int handlerPriority = (int)p.getNumber("handler.priority", Thread.MIN_PRIORITY); 26 | final int handlerCount = (int)p.getNumber("handler.count", 1); 27 | final int queueSize = (int)p.getNumber("handler.queue-size", 500); 28 | final FileMatcher[] acceptList = parseFileMatchers(p.getProperty("include")); 29 | final FileMatcher[] rejectList = parseFileMatchers(p.getProperty("exclude", "**/gumshoe/**,*.jar,*.class")); 30 | final FileMatcherSeries socketFilter = new FileMatcherSeries(acceptList, rejectList); 31 | final boolean statsEnabled = p.isTrue("handler.stats-enabled", false); 32 | return new FileIOMonitor(socketFilter, queueSize, handlerPriority, handlerCount, statsEnabled); 33 | } 34 | 35 | protected static FileMatcher[] parseFileMatchers(String csv) throws ParseException { 36 | if(csv==null || csv.trim().equals("")) { 37 | return new FileMatcher[0]; 38 | } 39 | 40 | final String[] pathDescriptions = csv.split(","); 41 | final int len = pathDescriptions.length; 42 | final FileMatcher[] matchers = new FileMatcher[len]; 43 | for(int i=0;i=0 file chars (but not /) 32 | .replaceAll("\\?", "[^/]"); // "?" matches one file char (not /) 33 | 34 | // if original was simple filename (no dir info) then assume any dir is ok 35 | final String pathRegex = wildcardPattern.contains("/") ? regex : ("(.*/)?"+regex); 36 | 37 | // on windows, reverse all the slashes 38 | final String osRegex = IS_WINDOWS ? pathRegex.replaceAll("/", "\\\\") : pathRegex; 39 | 40 | this.pattern = Pattern.compile(osRegex); 41 | } 42 | 43 | @Override 44 | public boolean matches(String path) { 45 | return pattern.matcher(path).matches(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/hook/IoTraceAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.hook; 2 | 3 | import java.net.InetAddress; 4 | import java.net.InetSocketAddress; 5 | import java.net.SocketAddress; 6 | 7 | /** adapter class to simplify implementation of IoTraceListener */ 8 | public class IoTraceAdapter implements IoTraceListener { 9 | public Object socketReadBegin() { return null; } 10 | public void socketReadEnd(Object context, InetAddress address, int port, int timeout, long bytesRead) { } 11 | public Object socketWriteBegin() { return null; } 12 | public void socketWriteEnd(Object context, InetAddress address, int port, long bytesWritten) { } 13 | public Object fileReadBegin(String path) { return null; } 14 | public void fileReadEnd(Object context, long bytesRead) { } 15 | public Object fileWriteBegin(String path) { return null; } 16 | public void fileWriteEnd(Object context, long bytesWritten) { } 17 | public Object datagramReadBegin() { return null; } 18 | public void datagramReadEnd(Object context, SocketAddress address, long bytesRead) { } 19 | public Object datagramWriteBegin() { return null; } 20 | public void datagramWriteEnd(Object context, SocketAddress address, long bytesWritten) { } 21 | 22 | // alt end methods for *some* NIO calls 23 | public void socketReadEnd(Object context, SocketAddress address, long bytesRead) { 24 | if(context!=null && address instanceof InetSocketAddress) { 25 | final InetSocketAddress ipAndPort = (InetSocketAddress)address; 26 | socketReadEnd(context, ipAndPort.getAddress(), ipAndPort.getPort(), 0, bytesRead); 27 | } 28 | } 29 | 30 | public void socketWriteEnd(Object context, SocketAddress address, long bytesWritten) { 31 | if(context!=null && address instanceof InetSocketAddress) { 32 | final InetSocketAddress ipAndPort = (InetSocketAddress)address; 33 | socketWriteEnd(context, ipAndPort.getAddress(), ipAndPort.getPort(), bytesWritten); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/hook/IoTraceHandler.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.hook; 2 | 3 | import java.net.InetAddress; 4 | import java.net.SocketAddress; 5 | 6 | public class IoTraceHandler { 7 | private static IoTraceListener NULL_OBJECT = new IoTraceAdapter(); 8 | private static IoTraceListener DELEGATE = NULL_OBJECT; 9 | 10 | public synchronized static void addTrace(IoTraceListener delegate) throws Exception { 11 | if(DELEGATE==NULL_OBJECT) { 12 | DELEGATE = delegate; 13 | } else if(DELEGATE instanceof IoTraceMultiplexer) { 14 | final IoTraceMultiplexer multi = (IoTraceMultiplexer) DELEGATE; 15 | multi.addDelegate(delegate); 16 | } else { 17 | final IoTraceMultiplexer multi = new IoTraceMultiplexer(); 18 | multi.addDelegate(DELEGATE); 19 | multi.addDelegate(delegate); 20 | DELEGATE = multi; 21 | } 22 | } 23 | 24 | public static void removeTrace(IoTraceListener delegate) throws Exception { 25 | if(DELEGATE==delegate) { 26 | DELEGATE = NULL_OBJECT; 27 | } else if(DELEGATE instanceof IoTraceMultiplexer) { 28 | final IoTraceMultiplexer multi = (IoTraceMultiplexer) DELEGATE; 29 | multi.removeDelegate(delegate); 30 | } else { 31 | throw new IllegalArgumentException("unable to remove, that IoTraceListener was not installed: " + delegate); 32 | } 33 | } 34 | 35 | public static IoTraceListener getTrace() { 36 | return DELEGATE; 37 | } 38 | 39 | ///// 40 | 41 | public static Object socketReadBegin() { 42 | return DELEGATE.socketReadBegin(); 43 | } 44 | 45 | public static void socketReadEnd(Object context, InetAddress address, int port, 46 | int timeout, long bytesRead) { 47 | DELEGATE.socketReadEnd(context, address, port, timeout, bytesRead); 48 | } 49 | 50 | public static Object socketWriteBegin() { 51 | return DELEGATE.socketWriteBegin(); 52 | } 53 | 54 | public static void socketWriteEnd(Object context, InetAddress address, int port, 55 | long bytesWritten) { 56 | DELEGATE.socketWriteEnd(context, address, port, bytesWritten); 57 | } 58 | 59 | public static void socketReadEnd(Object context, SocketAddress address, 60 | long bytesRead) { 61 | DELEGATE.socketReadEnd(context, address, bytesRead); 62 | } 63 | 64 | public static void socketWriteEnd(Object context, SocketAddress address, 65 | long bytesWritten) { 66 | DELEGATE.socketWriteEnd(context, address, bytesWritten); 67 | } 68 | 69 | public static Object datagramReadBegin() { 70 | return DELEGATE.datagramReadBegin(); 71 | } 72 | 73 | public static void datagramReadEnd(Object context, SocketAddress address, 74 | long bytesRead) { 75 | DELEGATE.datagramReadEnd(context, address, bytesRead); 76 | } 77 | 78 | public static Object datagramWriteBegin() { 79 | return DELEGATE.datagramWriteBegin(); 80 | } 81 | 82 | public static void datagramWriteEnd(Object context, SocketAddress address, 83 | long bytesWritten) { 84 | DELEGATE.datagramWriteEnd(context, address, bytesWritten); 85 | } 86 | 87 | public static Object fileReadBegin(String path) { 88 | return DELEGATE.fileReadBegin(path); 89 | } 90 | 91 | public static void fileReadEnd(Object context, long bytesRead) { 92 | DELEGATE.fileReadEnd(context, bytesRead); 93 | } 94 | 95 | public static Object fileWriteBegin(String path) { 96 | return DELEGATE.fileWriteBegin(path); 97 | } 98 | 99 | public static void fileWriteEnd(Object context, long bytesWritten) { 100 | DELEGATE.fileWriteEnd(context, bytesWritten); 101 | } 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/hook/IoTraceListener.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.hook; 2 | 3 | import java.net.InetAddress; 4 | import java.net.SocketAddress; 5 | 6 | /** public interface for delegate to plug into sun.misc.IoTrace */ 7 | public interface IoTraceListener { 8 | public Object socketReadBegin(); 9 | public void socketReadEnd(Object context, InetAddress address, int port, int timeout, long bytesRead); 10 | public Object socketWriteBegin(); 11 | public void socketWriteEnd(Object context, InetAddress address, int port, long bytesWritten); 12 | 13 | public void socketReadEnd(Object context, SocketAddress address, long bytesRead); 14 | public void socketWriteEnd(Object context, SocketAddress address, long bytesWritten); 15 | 16 | public Object datagramReadBegin(); 17 | public void datagramReadEnd(Object context, SocketAddress address, long bytesRead); 18 | public Object datagramWriteBegin(); 19 | public void datagramWriteEnd(Object context, SocketAddress address, long bytesWritten); 20 | 21 | public Object fileReadBegin(String path); 22 | public void fileReadEnd(Object context, long bytesRead); 23 | public Object fileWriteBegin(String path); 24 | public void fileWriteEnd(Object context, long bytesWritten); 25 | 26 | // marking interfaces 27 | public interface SocketListener extends IoTraceListener { } 28 | public interface DatagramListener extends IoTraceListener { } 29 | public interface FileListener extends IoTraceListener { } 30 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/io/IOAccumulator.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.io; 2 | 3 | import com.dell.gumshoe.stack.Stack; 4 | import com.dell.gumshoe.stack.StackFilter; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StackStatisticSource; 7 | 8 | import java.util.Collections; 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | import java.util.concurrent.ConcurrentMap; 12 | 13 | /** create hierarchical analysis of socket IO versus stack 14 | * with total IO at each frame and 15 | * link from frame to multiple next frames below 16 | */ 17 | public abstract class IOAccumulator implements IOListener, StackStatisticSource { 18 | private ConcurrentMap totals = new ConcurrentHashMap<>(); 19 | private StackFilter filter; 20 | 21 | public IOAccumulator(StackFilter filter) { 22 | this.filter = filter; 23 | } 24 | 25 | public void setFilter(StackFilter filter) { 26 | this.filter = filter; 27 | totals = new ConcurrentHashMap<>(); 28 | } 29 | 30 | @Override 31 | public void ioHasCompleted(IOEvent event) { 32 | Stack stack = event.getStack().applyFilter(filter); 33 | final T total = getAccumulator(stack); 34 | total.add(event); 35 | } 36 | 37 | private T getAccumulator(Stack key) { 38 | final ConcurrentMap totalLocalCopy = this.totals; 39 | final T entry = totalLocalCopy.get(key); 40 | if(entry!=null) { 41 | return entry; 42 | } 43 | totalLocalCopy.putIfAbsent(key, newAdder()); 44 | return totalLocalCopy.get(key); 45 | } 46 | 47 | public abstract T newAdder(); 48 | 49 | /** THREAD SAFETY: the object returned may be modified while you are using it. 50 | * callers should not keep this reference long, 51 | * and an entry may continue to be modified after fetched. 52 | */ 53 | @Override 54 | public Map getStats() { 55 | return Collections.unmodifiableMap(totals); 56 | } 57 | 58 | @Override 59 | public void reset() { 60 | totals = new ConcurrentHashMap<>(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/io/IOEvent.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.io; 2 | 3 | import com.dell.gumshoe.stack.Stack; 4 | 5 | public abstract class IOEvent { 6 | private final Stack stack; 7 | private long elapsed; 8 | 9 | private String target; 10 | private long bytes; 11 | 12 | protected IOEvent() { 13 | elapsed = System.currentTimeMillis(); 14 | stack = new Stack(); 15 | } 16 | 17 | public void complete(long bytes) { 18 | elapsed = System.currentTimeMillis() - elapsed; 19 | this.bytes = bytes; 20 | } 21 | 22 | protected void setTarget(String target) { this.target = target; } 23 | 24 | public Stack getStack() { return stack; } 25 | public String getTarget() { return target; } 26 | public long getElapsed() { return elapsed; } 27 | public long getBytes() { return bytes; } 28 | public abstract boolean isRead(); 29 | public abstract boolean isWrite(); 30 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/io/IOListener.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.io; 2 | 3 | 4 | public interface IOListener { 5 | public void ioHasCompleted(IOEvent event); 6 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/io/IOMBean.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.io; 2 | 3 | 4 | public interface IOMBean { 5 | public void setEnabled(boolean enabled); 6 | public boolean isEnabled(); 7 | 8 | public String getReport(); 9 | public void reset(); 10 | 11 | public void setReportingFrequency(long millis); 12 | public long getReportingFrequency(); 13 | 14 | public void setShutdownReportEnabled(boolean enabled); 15 | public boolean isShutdownReportEnabled(); 16 | 17 | public void setHandlerThreadCount(int count); 18 | public int getHandlerThreadCount(); 19 | 20 | public void setHandlerPriority(int value); 21 | public int getHandlerPriority(); 22 | 23 | public int getEventQueueSize(); 24 | 25 | public String getQueueStats(); 26 | public void resetQueueCounters(); 27 | 28 | public void setQueueStatisticsEnabled(boolean enabled); 29 | public boolean isQueueStatisticsEnabled(); 30 | } 31 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/AddressMatcher.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import java.net.SocketAddress; 4 | 5 | public interface AddressMatcher { 6 | boolean matches(byte[] addressBytes, int port); 7 | boolean matches(SocketAddress address); 8 | } 9 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/DatagramEvent.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.io.IOEvent; 4 | 5 | import java.net.SocketAddress; 6 | 7 | public abstract class DatagramEvent extends IOEvent { 8 | 9 | public void complete(SocketAddress address, long bytes) { 10 | complete(bytes); 11 | setTarget( address.toString() ); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/DatagramIOAccumulator.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.io.IOAccumulator; 4 | import com.dell.gumshoe.stack.StackFilter; 5 | 6 | /** create hierarchical analysis of socket IO versus stack 7 | * with total IO at each frame and 8 | * link from frame to multiple next frames below 9 | */ 10 | public class DatagramIOAccumulator extends IOAccumulator { 11 | 12 | public DatagramIOAccumulator(StackFilter filter) { 13 | super(filter); 14 | } 15 | 16 | @Override 17 | public DatagramIODetailAdder newAdder() { 18 | return new DatagramIODetailAdder(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/DatagramIODetailAdder.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.io.IOEvent; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StatisticAdder; 7 | 8 | public class DatagramIODetailAdder extends IODetailAdder { 9 | @Override 10 | public String getType() { 11 | return ProbeManager.DATAGRAM_IO_LABEL; 12 | } 13 | 14 | @Override 15 | public StatisticAdder newInstance() { 16 | return new DatagramIODetailAdder(); 17 | } 18 | 19 | public static DatagramIODetailAdder fromString(String line) { 20 | final DatagramIODetailAdder out = new DatagramIODetailAdder(); 21 | try { 22 | out.setFromString(line); 23 | } catch(Exception e) { 24 | return null; 25 | } 26 | return out; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/DatagramIOProbe.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.io.IOAccumulator; 4 | import com.dell.gumshoe.io.IOMonitor; 5 | import com.dell.gumshoe.io.IOProbe; 6 | import com.dell.gumshoe.stack.StackFilter; 7 | import com.dell.gumshoe.util.Configuration; 8 | 9 | import java.text.ParseException; 10 | 11 | public class DatagramIOProbe extends IOProbe { 12 | public static final String LABEL = "datagram-io"; 13 | 14 | public DatagramIOProbe(ProbeServices services) { 15 | super(services); 16 | } 17 | 18 | @Override 19 | public String getLabel() { 20 | return LABEL; 21 | } 22 | 23 | @Override 24 | protected IOMonitor createMonitor(Configuration cfg) throws Exception { 25 | final int handlerPriority = (int)cfg.getNumber("handler.priority", Thread.MIN_PRIORITY); 26 | final int handlerCount = (int)cfg.getNumber("handler.count", 1); 27 | final int queueSize = (int)cfg.getNumber("handler.queue-size", 500); 28 | final boolean includeNIO = cfg.isTrue("use-nio-hooks", false); 29 | final AddressMatcher[] acceptList = parseSocketMatchers(cfg.getProperty("include")); 30 | final AddressMatcher[] rejectList = parseSocketMatchers(cfg.getProperty("exclude", "127.0.0.1/32:*")); 31 | final MultiAddressMatcher socketFilter = new MultiAddressMatcher(acceptList, rejectList); 32 | final boolean statsEnabled = cfg.isTrue("handler.stats-enabled", false); 33 | 34 | // TODO: detect OS; print warning if not set and OS is windows 35 | final String usingOnly = cfg.getProperty("using-only", "unicast"); 36 | final boolean useMulticast = "multicast".equals(usingOnly); 37 | 38 | return new DatagramIOMonitor(socketFilter, includeNIO, useMulticast, queueSize, handlerPriority, handlerCount, statsEnabled); 39 | } 40 | 41 | private static AddressMatcher[] parseSocketMatchers(String csv) throws ParseException { 42 | if(csv==null || csv.trim().equals("")) { 43 | return new AddressMatcher[0]; 44 | } 45 | 46 | final String[] addressDescriptions = csv.split(","); 47 | final int len = addressDescriptions.length; 48 | final AddressMatcher[] matchers = new AddressMatcher[len]; 49 | for(int i=0;i { 11 | 12 | public SocketIOAccumulator(StackFilter filter) { 13 | super(filter); 14 | } 15 | 16 | @Override 17 | public SocketIODetailAdder newAdder() { 18 | return new SocketIODetailAdder(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/SocketIODetailAdder.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.io.IOEvent; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StatisticAdder; 7 | 8 | public class SocketIODetailAdder extends IODetailAdder { 9 | @Override 10 | public String getType() { 11 | return ProbeManager.SOCKET_IO_LABEL; 12 | } 13 | 14 | @Override 15 | public StatisticAdder newInstance() { 16 | return new SocketIODetailAdder(); 17 | } 18 | 19 | public static SocketIODetailAdder fromString(String line) { 20 | final SocketIODetailAdder out = new SocketIODetailAdder(); 21 | try { 22 | out.setFromString(line); 23 | } catch(Exception e) { 24 | return null; 25 | } 26 | return out; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/SocketIOMBean.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | 4 | public interface SocketIOMBean { 5 | public void setEnabled(boolean enabled); 6 | public boolean isEnabled(); 7 | 8 | public String getReport(); 9 | public void reset(); 10 | 11 | public void setReportingFrequency(long millis); 12 | public long getReportingFrequency(); 13 | 14 | public void setShutdownReportEnabled(boolean enabled); 15 | public boolean isShutdownReportEnabled(); 16 | } 17 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/SocketIOMonitor.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import static com.dell.gumshoe.util.Output.error; 4 | 5 | import com.dell.gumshoe.hook.IoTraceListener.SocketListener; 6 | import com.dell.gumshoe.io.IOMonitor; 7 | 8 | import java.net.InetAddress; 9 | 10 | /** monitor socket IO and report each as an event to registered listeners 11 | */ 12 | public class SocketIOMonitor extends IOMonitor implements SocketListener { 13 | private final AddressMatcher socketFilter; 14 | private boolean useNIOHooks; 15 | public SocketIOMonitor() { 16 | this(SubnetAddress.ANY, false, 500, Thread.MIN_PRIORITY, 1, false); 17 | } 18 | 19 | public SocketIOMonitor(AddressMatcher socketFilter, boolean useNIOHooks, int queueSize, int priority, int count, boolean statsEnabled) { 20 | this.socketFilter = socketFilter; 21 | this.useNIOHooks = useNIOHooks; 22 | setEventQueueSize(queueSize); 23 | setThreadCount(count); 24 | setThreadPriority(priority); 25 | setQueueStatisticsEnabled(statsEnabled); 26 | } 27 | 28 | @Override 29 | public void initializeProbe() throws Exception { 30 | if(useNIOHooks) { 31 | final String className = System.getProperty("java.nio.channels.spi.SelectorProvider"); 32 | if( ! IoTraceSelectorProvider.class.getName().equals(className)) { 33 | error("System property must be set: java.nio.channels.spi.SelectorProvider. NIO tracing will be incomplete"); 34 | } else { 35 | IoTraceSelectorProvider.setSocketTraceEnabled(true); 36 | } 37 | } 38 | super.initializeProbe(); 39 | } 40 | 41 | ///// 42 | 43 | @Override 44 | public Object socketReadBegin() { 45 | return SocketReadEvent.begin(); 46 | } 47 | 48 | @Override 49 | public Object socketWriteBegin() { 50 | return SocketWriteEvent.begin(); 51 | } 52 | 53 | @Override 54 | public void socketReadEnd(Object context, InetAddress address, int port, int timeout, long bytesRead) { 55 | handleEvent(context, address, port, bytesRead); 56 | } 57 | 58 | @Override 59 | public void socketWriteEnd(Object context, InetAddress address, int port, long bytesWritten) { 60 | handleEvent(context, address, port, bytesWritten); 61 | } 62 | 63 | private void handleEvent(Object context, InetAddress address, int port, long bytes) { 64 | if(socketFilter.matches(address.getAddress(), port)) { 65 | final SocketEvent operation = (SocketEvent)context; 66 | operation.complete(address, port, bytes); 67 | queueEvent(operation); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/SocketIOProbe.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.io.IOAccumulator; 4 | import com.dell.gumshoe.io.IOMonitor; 5 | import com.dell.gumshoe.io.IOProbe; 6 | import com.dell.gumshoe.stack.StackFilter; 7 | import com.dell.gumshoe.util.Configuration; 8 | 9 | import java.text.ParseException; 10 | 11 | public class SocketIOProbe extends IOProbe { 12 | public static final String LABEL = "socket-io"; 13 | 14 | public SocketIOProbe(ProbeServices services) { 15 | super(services); 16 | } 17 | 18 | @Override 19 | public String getLabel() { 20 | return LABEL; 21 | } 22 | 23 | @Override 24 | protected IOMonitor createMonitor(Configuration p) throws ParseException { 25 | final int handlerPriority = (int)p.getNumber("handler.priority", Thread.MIN_PRIORITY); 26 | final int handlerCount = (int)p.getNumber("handler.count", 1); 27 | final int queueSize = (int)p.getNumber("handler.queue-size", 500); 28 | final boolean includeNIO = p.isTrue("use-nio-hooks", false); 29 | final AddressMatcher[] acceptList = parseSocketMatchers(p.getProperty("include")); 30 | final AddressMatcher[] rejectList = parseSocketMatchers(p.getProperty("exclude", "127.0.0.1/32:*")); 31 | final MultiAddressMatcher socketFilter = new MultiAddressMatcher(acceptList, rejectList); 32 | final boolean statsEnabled = p.isTrue("handler.stats-enabled", false); 33 | return new SocketIOMonitor(socketFilter, includeNIO, queueSize, handlerPriority, handlerCount, statsEnabled); 34 | } 35 | 36 | private static AddressMatcher[] parseSocketMatchers(String csv) throws ParseException { 37 | if(csv==null || csv.trim().equals("")) { 38 | return new AddressMatcher[0]; 39 | } 40 | 41 | final String[] addressDescriptions = csv.split(","); 42 | final int len = addressDescriptions.length; 43 | final AddressMatcher[] matchers = new AddressMatcher[len]; 44 | for(int i=0;i255) { 21 | throw new ParseException("invalid IP/mask:port value: " + description,i); 22 | } 23 | targetAddress[i] = addressByte.byteValue(); 24 | } 25 | 26 | final int bits = ((Number)fields[4]).byteValue(); 27 | long bitvalue = 0xFFFFFFFF ^ ((1L<<(32-bits)) - 1); 28 | mask[0] = (byte) (bitvalue>>24 & 0xFF); 29 | mask[1] = (byte) (bitvalue>>16 & 0xFF); 30 | mask[2] = (byte) (bitvalue>>8 & 0xFF); 31 | mask[3] = (byte) (bitvalue & 0xFF); 32 | 33 | final String portField = (String)fields[5]; 34 | if(portField.equals("*")) { 35 | targetPort = null; 36 | } else { 37 | targetPort = Integer.parseInt(portField); 38 | if(targetPort<0 || targetPort>65535) { 39 | throw new ParseException("invalid port: " + description, 0); 40 | } 41 | } 42 | } 43 | 44 | @Override 45 | public boolean matches(byte[] addressBytes, int port) { 46 | for(int i=0;i<4;i++) { 47 | if(((addressBytes[i]^targetAddress[i]) & mask[i]) > 0) { 48 | return false; 49 | } 50 | } 51 | 52 | return targetPort==null || targetPort.equals(port); 53 | } 54 | 55 | public boolean matches(SocketAddress address) { 56 | if(address instanceof InetSocketAddress) { 57 | final InetSocketAddress ipAndPort = (InetSocketAddress)address; 58 | return matches(ipAndPort.getAddress().getAddress(), ipAndPort.getPort()); 59 | } else { 60 | // ipv6 or some esoteric network 61 | return false; 62 | } 63 | 64 | } 65 | 66 | public String toString() { 67 | final StringBuilder out = new StringBuilder(); 68 | for(int i=0;i<4;i++) { 69 | if(i>0) out.append("."); 70 | out.append(0xFF & targetAddress[i]); 71 | } 72 | out.append("/"); 73 | for(int i=0;i<4;i++) { 74 | if(i>0) out.append("."); 75 | out.append(0xFF & mask[i]); 76 | } 77 | out.append(":"); 78 | if(targetPort==null) out.append("*"); 79 | else out.append(targetPort); 80 | return out.toString(); 81 | } 82 | 83 | private static class MatchAny implements AddressMatcher { 84 | public boolean matches(byte[] unused, int port) { return true; } 85 | public boolean matches(SocketAddress address) { return true; } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/UnclosedSocketProbe.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.Probe; 4 | import com.dell.gumshoe.stack.Stack; 5 | import com.dell.gumshoe.stack.StackFilter; 6 | import com.dell.gumshoe.stats.ValueReporter; 7 | import com.dell.gumshoe.util.Configuration; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.PrintStream; 11 | import java.util.Map; 12 | 13 | public class UnclosedSocketProbe extends Probe implements SocketCloseMonitorMBean { 14 | public static final String LABEL = "open-sockets"; 15 | 16 | private SocketCloseMonitor monitor; 17 | private ValueReporter reporter; 18 | 19 | public UnclosedSocketProbe(ProbeServices services) { 20 | super(services); 21 | } 22 | 23 | @Override 24 | public ValueReporter getReporter() { return reporter; } 25 | 26 | ///// 27 | 28 | @Override 29 | public void initialize(Configuration cfg) throws Exception { 30 | final boolean shutdownReportEnabled = cfg.isTrue("onshutdown", false); 31 | final Long periodicFrequency = cfg.getNumber("period"); 32 | final boolean periodicReportEnabled = periodicFrequency!=null; 33 | final boolean reportEnabled = shutdownReportEnabled || periodicReportEnabled; 34 | 35 | // jmx enabled if explicit name, explicit property, or some reporting enabled 36 | final boolean jmxEnabled = 37 | getMBeanName(cfg)!=null || cfg.isTrue("mbean", reportEnabled); 38 | final String mbeanName = jmxEnabled ? getMBeanName(cfg, getClass()) : null; 39 | 40 | final boolean enabled = cfg.isTrue("enabled", reportEnabled || jmxEnabled); 41 | if( ! enabled) { return; } 42 | 43 | final long minAge = cfg.getNumber("age", 30000); 44 | final int clearCount = (int) cfg.getNumber("check-interval", 100); 45 | 46 | final StackFilter stackFilter = createStackFilter(cfg); 47 | 48 | final PrintStream out = getOutput(cfg); 49 | initialize(shutdownReportEnabled, periodicFrequency, minAge, clearCount, stackFilter, jmxEnabled?mbeanName:null, out); 50 | } 51 | 52 | public void initialize(boolean shutdownReportEnabled, Long reportPeriod, final long minAge, int clearCount, 53 | StackFilter filter, String mbeanName, final PrintStream out) throws Exception { 54 | if(monitor!=null) throw new IllegalStateException("probe is already installed"); 55 | 56 | monitor = new SocketCloseMonitor(minAge, filter); 57 | monitor.setClearClosedSocketsInterval(clearCount); 58 | 59 | reporter = new ValueReporter(LABEL, monitor); 60 | if(out!=null) { 61 | reporter.addStreamReporter(out); 62 | } 63 | 64 | if(shutdownReportEnabled) { 65 | addShutdownHook(reporter.getShutdownHook()); 66 | } 67 | 68 | if(reportPeriod!=null) { 69 | reporter.scheduleReportTimer(getTimer(), reportPeriod); 70 | } 71 | 72 | if(mbeanName!=null) { 73 | installMBean(mbeanName, this, SocketCloseMonitorMBean.class); 74 | } 75 | 76 | monitor.initializeProbe(); 77 | } 78 | 79 | ///// 80 | 81 | @Override public boolean isEnabled() { return monitor.isEnabled(); } 82 | @Override public void setEnabled(boolean enabled) { monitor.setEnabled(enabled); } 83 | 84 | @Override 85 | public String getReport(long age) { 86 | final ByteArrayOutputStream rawOut = new ByteArrayOutputStream(); 87 | final PrintStream out = new PrintStream(rawOut); 88 | final ValueReporter.StreamReporter streamer = getReporter().new StreamReporter(out); 89 | final Map values = monitor.getStats(age); 90 | streamer.statsReported(values); 91 | return rawOut.toString(); 92 | } 93 | 94 | @Override 95 | public void setReportingFrequency(long millis) { 96 | getReporter().scheduleReportTimer(getTimer(), millis); 97 | } 98 | 99 | @Override 100 | public long getReportingFrequency() { 101 | return getReporter().getReportTimerFrequency(); 102 | } 103 | 104 | @Override 105 | public void setClearClosedSocketsInterval(int numberOfSockets) { 106 | monitor.setClearClosedSocketsInterval(numberOfSockets); 107 | } 108 | 109 | @Override 110 | public int getClearClosedSocketsInterval() { 111 | return monitor.getClearClosedSocketsInterval(); 112 | } 113 | 114 | @Override 115 | public void setShutdownReportEnabled(boolean enabled) { 116 | if(enabled) { 117 | addShutdownHook(getReporter().getShutdownHook()); 118 | } else { 119 | removeShutdownHook(getReporter().getShutdownHook()); 120 | } 121 | } 122 | 123 | @Override 124 | public boolean isShutdownReportEnabled() { 125 | return isShutdownHookEnabled(getReporter().getShutdownHook()); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/network/UnclosedStats.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.network; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.network.SocketCloseMonitor.SocketImplDecorator; 5 | import com.dell.gumshoe.stats.StatisticAdder; 6 | 7 | import java.text.MessageFormat; 8 | 9 | /** thread-safety: toString and fromString are synchronized on FORMAT, 10 | * contention not likely in current usage so this is chosen 11 | * to reduce overhead of creating lots of new MessageFormat instances. 12 | * consider change if use-case is different. 13 | */ 14 | public class UnclosedStats implements StatisticAdder { 15 | public static final MessageFormat FORMAT = 16 | new MessageFormat("{0,number,#} sockets, oldest {1,number,#} ms"); 17 | 18 | private int count; 19 | private long maxAge = 0; 20 | 21 | public int getCount() { return count; } 22 | public long getMaxAge() { return maxAge; } 23 | 24 | public void add(long now, SocketImplDecorator item) { 25 | this.count++; 26 | final long age = now - item.openTime; 27 | this.maxAge = Math.max(this.maxAge, age); 28 | } 29 | 30 | @Override 31 | public void add(StatisticAdder value) { 32 | add((UnclosedStats)value); 33 | } 34 | 35 | @Override 36 | public void add(UnclosedStats value) { 37 | this.count += value.count; 38 | this.maxAge = Math.max(this.maxAge, value.maxAge); 39 | } 40 | 41 | @Override 42 | public StatisticAdder newInstance() { 43 | return new UnclosedStats(); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | final Object[] arg = { count, maxAge }; 49 | synchronized(FORMAT) { 50 | return FORMAT.format(arg); 51 | } 52 | } 53 | 54 | public static StatisticAdder fromString(String value) { 55 | final Object[] fields; 56 | try { 57 | synchronized(FORMAT) { 58 | fields = FORMAT.parse(value); 59 | } 60 | } catch(Exception e) { 61 | return null; 62 | } 63 | final UnclosedStats out = new UnclosedStats(); 64 | out.count = ((Number)fields[0]).intValue(); 65 | out.maxAge = ((Number)fields[1]).longValue(); 66 | return out; 67 | } 68 | 69 | @Override 70 | public String getType() { 71 | return ProbeManager.UNCLOSED_SOCKET_LABEL; 72 | } 73 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stack/FilterSequence.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stack; 2 | 3 | import java.util.List; 4 | 5 | /** create custom filters 6 | * 7 | */ 8 | public class FilterSequence implements StackFilter { 9 | private final boolean withOriginal; 10 | private final List filters; 11 | 12 | public FilterSequence(boolean withOriginal, List filters) { 13 | this.withOriginal = withOriginal; 14 | this.filters = filters; 15 | } 16 | 17 | @Override 18 | public int filter(StackTraceElement[] mutable, int origSize) { 19 | final StackTraceElement[] orig = withOriginal ? mutable.clone() : null; 20 | int size = origSize; 21 | for(StackFilter filter : filters) { 22 | size = filter.filter(mutable, size); 23 | if(size==0) break; 24 | } 25 | if(size==0 && withOriginal) { 26 | System.arraycopy(orig, 0, mutable, 0, origSize); 27 | return origSize; 28 | } 29 | return size; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stack/FrameMatcher.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stack; 2 | 3 | 4 | public abstract class FrameMatcher implements StackFilter { 5 | public abstract boolean matches(StackTraceElement frame); 6 | 7 | @Override 8 | public int filter(StackTraceElement[] mutable, int size) { 9 | int newSize = 0; 10 | for(int index=0;index=0 ? fullName.substring(0, index-1) : fullName; 22 | return new StackTraceElement(modifiedName, "", orig.getFileName(), -1); 23 | } 24 | }, 25 | NO_CLASSES { 26 | public StackTraceElement modify(StackTraceElement orig) { 27 | final String fullName = orig.getClassName(); 28 | final int index = fullName.lastIndexOf('.'); 29 | final String modifiedName = index>=0 ? fullName.substring(0, index-1) : fullName; 30 | return new StackTraceElement(modifiedName, "", null, -1); 31 | } 32 | }; 33 | 34 | public abstract StackTraceElement modify(StackTraceElement orig); 35 | } 36 | 37 | private final Level level; 38 | 39 | public MinutiaFilter(String level) { 40 | this(Level.valueOf(level)); 41 | } 42 | public MinutiaFilter(Level level) { 43 | this.level = level; 44 | } 45 | 46 | @Override 47 | public int filter(StackTraceElement[] buffer, int size) { 48 | for(int i=0;imatchStart) { 26 | // we have a 1+ matches of the source sequence 27 | // target: buffer[matchStart]...buffer[matchEnd-1] 28 | // so remove them from buffer and adjust the loop accordingly 29 | final int remaining = outSize-matchEnd; 30 | if(remaining>0) { 31 | System.arraycopy(buffer, matchEnd, buffer, matchStart, remaining); 32 | } 33 | outSize -= matchEnd-matchStart; 34 | } 35 | } 36 | } 37 | return outSize; 38 | } 39 | 40 | private int getMatchEnd(StackTraceElement[] buffer, int source, int target, int length) { 41 | while(matches(buffer, source, target, length)) { 42 | target += length; 43 | } 44 | return target; 45 | } 46 | 47 | private boolean matches(StackTraceElement[] buffer, int source, int target, int length) { 48 | for(int i=0;i0) { 22 | System.arraycopy(buffer, 0, filteredStack, 0, size); 23 | } 24 | return new Stack(filteredStack); 25 | } 26 | 27 | public StackTraceElement[] getFrames() { 28 | return frames.clone(); 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | return Arrays.hashCode(frames); 34 | } 35 | 36 | @Override 37 | public boolean equals(Object obj) { 38 | if( ! (obj instanceof Stack)) return false; 39 | Stack that = (Stack)obj; 40 | return Arrays.equals(this.frames, that.frames); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | final StringBuilder out = new StringBuilder(); 46 | for(StackTraceElement frame : frames) { 47 | out.append(FRAME_PREFIX).append(frame).append("\n"); 48 | } 49 | return out.toString(); 50 | } 51 | 52 | public boolean isEmpty() { 53 | return frames.length==0; 54 | } 55 | 56 | public static StackTraceElement parseFrame(String line) { 57 | 58 | final String filename; 59 | final int lineNumber; 60 | if(line.endsWith("(Unknown Source)")) { 61 | filename = null; 62 | lineNumber = -1; 63 | } else if(line.endsWith("(Native Method)")) { 64 | filename = null; 65 | lineNumber = -2; 66 | } else if(line.contains(":")) { 67 | final String[] parts = line.split("[(:)]"); 68 | filename = parts[1]; 69 | lineNumber = Integer.parseInt(parts[2]); 70 | } else { 71 | final String[] parts = line.split("[(:)]"); 72 | filename = parts[1]; 73 | lineNumber = -1; 74 | } 75 | 76 | String[] parts = line.split("[ (]+"); 77 | final String classAndMethod = parts[2]; 78 | parts = classAndMethod.split("\\."); 79 | final String methodName = parts[parts.length-1]; 80 | final String className = classAndMethod.substring(0, classAndMethod.length()-methodName.length()-1); 81 | 82 | return new StackTraceElement(className, methodName, filename, lineNumber); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stack/StackFilter.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stack; 2 | 3 | 4 | public interface StackFilter { 5 | public int filter(StackTraceElement[] buffer, int size); 6 | 7 | public static StackFilter NONE = new StackFilter() { 8 | @Override 9 | public int filter(StackTraceElement[] buffer, int size) { 10 | return size; 11 | } 12 | }; 13 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stack/StandardFilter.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stack; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class StandardFilter { 7 | public static Builder builder() { return new Builder(); } 8 | 9 | public static class Builder { 10 | private final List filters = new ArrayList<>(); 11 | private final List includePatterns = new ArrayList<>(); 12 | private final List excludePatterns = new ArrayList<>(); 13 | 14 | private boolean withOriginal = false; 15 | private StackFilter endFilter; 16 | private String frameMinutia; 17 | private Integer recursionDepth; 18 | private int recursionThreshold; 19 | 20 | private Builder() { } 21 | 22 | public Builder withFilter(StackFilter filter) { 23 | filters.add(filter); 24 | return this; 25 | } 26 | 27 | public Builder withOriginalIfBlank() { 28 | withOriginal = true; 29 | return this; 30 | } 31 | 32 | public Builder withExcludePlatform() { 33 | withExcludeClasses("sunw."); 34 | withExcludeClasses("sun."); 35 | withExcludeClasses("java."); 36 | withExcludeClasses("javax."); 37 | withExcludeClasses("com.dell.gumshoe."); 38 | return this; 39 | } 40 | 41 | public Builder withExcludeClasses(final String startingWith) { 42 | excludePatterns.add(startingWith); 43 | return this; 44 | } 45 | 46 | public Builder withOnlyClasses(final String startingWith) { 47 | includePatterns.add(startingWith); 48 | return this; 49 | } 50 | 51 | public Builder withEndsOnly(final int topCount, final int bottomCount) { 52 | if(topCount==0 && bottomCount==0) { return this; } 53 | 54 | endFilter = new StackFilter() { 55 | @Override 56 | public int filter(StackTraceElement[] buffer, int size) { 57 | final int maxSize = topCount + bottomCount; 58 | final int frameCount = Math.min(size, maxSize); 59 | if(frameCount 3 count 2 63 | // out: [ 0 1 2 7 8 ] 64 | System.arraycopy(buffer, size-bottomCount, buffer, topCount, bottomCount); 65 | } 66 | return frameCount; 67 | } 68 | }; 69 | return this; 70 | } 71 | 72 | public void withSimpleFrames(MinutiaFilter.Level level) { 73 | this.frameMinutia = level.toString(); 74 | } 75 | 76 | public void withSimpleFrames(String simplifyLevel) { 77 | this.frameMinutia = simplifyLevel; 78 | } 79 | 80 | public void withRecursionFilter(int depth, int threshold) { 81 | recursionDepth = depth; 82 | recursionThreshold = threshold; 83 | } 84 | public StackFilter build() { 85 | if( ! excludePatterns.isEmpty()) { 86 | filters.add(new FrameMatcher() { 87 | @Override 88 | public boolean matches(StackTraceElement frame) { 89 | final String className = frame.getClassName(); 90 | for(String pattern : excludePatterns) { 91 | if(className.startsWith(pattern)) { return false; } 92 | } 93 | return true; 94 | } 95 | }); 96 | } 97 | if( ! includePatterns.isEmpty()) { 98 | filters.add(new FrameMatcher() { 99 | @Override 100 | public boolean matches(StackTraceElement frame) { 101 | final String className = frame.getClassName(); 102 | for(String pattern : includePatterns) { 103 | if(className.startsWith(pattern)) { return true; } 104 | } 105 | return false; 106 | } 107 | }); 108 | } 109 | if(endFilter!=null) { 110 | filters.add(endFilter); 111 | } 112 | 113 | if(frameMinutia!=null && ! "NONE".equals(frameMinutia)) { 114 | filters.add(new MinutiaFilter(frameMinutia)); 115 | } 116 | 117 | if(recursionDepth!=null) { 118 | filters.add(new RecursionFilter(recursionDepth, recursionThreshold)); 119 | } 120 | 121 | return new FilterSequence(withOriginal, new ArrayList<>(filters)); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stats/IODetailAdder.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stats; 2 | 3 | import com.dell.gumshoe.io.IOEvent; 4 | 5 | import java.text.MessageFormat; 6 | import java.text.ParseException; 7 | import java.util.Arrays; 8 | import java.util.Set; 9 | import java.util.concurrent.ConcurrentSkipListSet; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import java.util.concurrent.atomic.AtomicLong; 12 | 13 | public abstract class IODetailAdder implements StatisticAdder { 14 | public static final MessageFormat FORMAT = 15 | new MessageFormat("{0,number,#} read ops {1,number,#} bytes in {2,number,#} ms, {3,number,#} write ops {4,number,#} bytes in {5,number,#} ms: [{6}]"); 16 | 17 | public final Set targets = new ConcurrentSkipListSet<>(); 18 | public final AtomicLong readBytes = new AtomicLong(); 19 | public final AtomicLong readTime = new AtomicLong(); 20 | public final AtomicInteger readCount = new AtomicInteger(); 21 | public final AtomicLong writeBytes = new AtomicLong(); 22 | public final AtomicLong writeTime = new AtomicLong(); 23 | public final AtomicInteger writeCount = new AtomicInteger(); 24 | 25 | @Override 26 | public void add(StatisticAdder that) { 27 | if(that instanceof IODetailAdder) { 28 | add((IODetailAdder)that); 29 | } 30 | } 31 | 32 | public void add(IODetailAdder that) { 33 | targets.addAll(that.targets); 34 | readBytes.addAndGet(that.readBytes.get()); 35 | readTime.addAndGet(that.readTime.get()); 36 | readCount.addAndGet(that.readCount.get()); 37 | writeBytes.addAndGet(that.writeBytes.get()); 38 | writeTime.addAndGet(that.writeTime.get()); 39 | writeCount.addAndGet(that.writeCount.get()); 40 | 41 | } 42 | @Override 43 | public void add(IOEvent event) { 44 | targets.add(event.getTarget()); 45 | if(event.isRead()) { 46 | readBytes.addAndGet(event.getBytes()); 47 | readTime.addAndGet(event.getElapsed()); 48 | readCount.incrementAndGet(); 49 | } else { 50 | writeBytes.addAndGet(event.getBytes()); 51 | writeTime.addAndGet(event.getElapsed()); 52 | writeCount.incrementAndGet(); 53 | } 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | final Object[] arg = { 59 | readCount.get(), readBytes.get(), readTime.get(), 60 | writeCount.get(), writeBytes.get(), writeTime.get(), 61 | targets.toString().replaceAll("[\\[\\]]", "") 62 | }; 63 | 64 | synchronized(FORMAT) { 65 | return FORMAT.format(arg); 66 | } 67 | } 68 | 69 | protected void setFromString(String line) throws ParseException { 70 | final Object[] fields; 71 | synchronized(FORMAT) { 72 | fields = FORMAT.parse(line); 73 | } 74 | 75 | this.readCount.set(((Number)fields[0]).intValue()); 76 | this.readBytes.set(((Number)fields[1]).longValue()); 77 | this.readTime.set(((Number)fields[2]).longValue()); 78 | this.writeCount.set(((Number)fields[3]).intValue()); 79 | this.writeBytes.set(((Number)fields[4]).longValue()); 80 | this.writeTime.set(((Number)fields[5]).longValue()); 81 | 82 | final String addressCSV = (String)fields[6]; 83 | this.targets.clear(); 84 | this.targets.addAll(Arrays.asList(addressCSV.replaceAll(" ", "").split(","))); 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stats/StackStatisticSource.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stats; 2 | 3 | import com.dell.gumshoe.stack.Stack; 4 | 5 | import java.util.Map; 6 | 7 | public interface StackStatisticSource { 8 | public Map getStats(); 9 | public void reset(); 10 | } 11 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/stats/StatisticAdder.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stats; 2 | 3 | 4 | public interface StatisticAdder { 5 | public void add(StatisticAdder value); 6 | public void add(V value); 7 | public StatisticAdder newInstance(); 8 | public String getType(); 9 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/thread/CpuUsageMBean.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.thread; 2 | 3 | 4 | public interface CpuUsageMBean { 5 | public long getJitter(); 6 | public void setJitter(long jitter); 7 | public long getDumpInterval(); 8 | public void setDumpInterval(long dumpInterval); 9 | public long getEffectiveInterval(); 10 | public long getAverageDumpTime(); 11 | 12 | public int getThreadPriority(); 13 | public void setThreadPriority(int threadPriority); 14 | 15 | 16 | public void setEnabled(boolean enabled); 17 | public boolean isEnabled(); 18 | 19 | public void setReportingFrequency(long millis); 20 | public long getReportingFrequency(); 21 | 22 | public void setShutdownReportEnabled(boolean enabled); 23 | public boolean isShutdownReportEnabled(); 24 | 25 | public String getReport(); 26 | public void reset(); 27 | } 28 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/thread/ThreadDetails.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.thread; 2 | 3 | import java.lang.Thread.State; 4 | import java.lang.management.ThreadInfo; 5 | 6 | /** information recorded about a single thread */ 7 | public class ThreadDetails { 8 | private final long cpuTime; // nanos 9 | private final long userTime; // nanos 10 | private final long blockedCount; 11 | private final long blockedTime; 12 | private final long waitedCount; 13 | private final long waitedTime; 14 | private final State state; 15 | 16 | public ThreadDetails(long cpuTime, long userTime, ThreadInfo item) { 17 | this.cpuTime = cpuTime; 18 | this.userTime = userTime; 19 | this.blockedCount = item.getBlockedCount(); 20 | this.blockedTime = item.getBlockedTime(); 21 | this.waitedCount = item.getWaitedCount(); 22 | this.waitedTime = item.getWaitedTime(); 23 | this.state = item.getThreadState(); 24 | } 25 | 26 | public ThreadDetails(long cpuTime, long userTime, long blockedCount, long blockedTime, long waitedCount, long waitedTime, State state) { 27 | this.cpuTime = cpuTime; 28 | this.userTime = userTime; 29 | this.blockedCount = blockedCount; 30 | this.blockedTime = blockedTime; 31 | this.waitedCount = waitedCount; 32 | this.waitedTime = waitedTime; 33 | this.state = state; 34 | } 35 | 36 | public long getCpuTime() { return cpuTime; } 37 | public long getUserTime() { return userTime; } 38 | public long getBlockedCount() { return blockedCount; } 39 | public long getBlockedTime() { return blockedTime; } 40 | public long getWaitedCount() { return waitedCount; } 41 | public long getWaitedTime() { return waitedTime; } 42 | public State getState() { return state; } 43 | } 44 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/thread/ThreadFilter.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.thread; 2 | 3 | import com.dell.gumshoe.stack.Stack; 4 | 5 | public interface ThreadFilter { 6 | public static ThreadFilter NONE = new ThreadFilter() { 7 | @Override 8 | public boolean useThread(Stack unused, ThreadDetails ignored) { return true; } 9 | }; 10 | 11 | public boolean useThread(Stack stack, ThreadDetails details); 12 | } 13 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/util/DefineLaterPrintStream.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.util; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | 5 | import java.io.IOException; 6 | import java.io.PrintStream; 7 | import java.util.Locale; 8 | import java.util.Map; 9 | 10 | /** probe may be started right as JVM starts, 11 | * but we may need to let the monitored program start and reach some hook 12 | * to programmatically define more interesting outputs. so this 13 | * 14 | */ 15 | public class DefineLaterPrintStream extends PrintStream { 16 | private final Map namedOutput; 17 | private String key; 18 | private volatile PrintStream delegate; 19 | 20 | public DefineLaterPrintStream(String key, Map namedOutput) { 21 | super(ProbeManager.NULL_OUTPUT_STREAM); 22 | this.namedOutput = namedOutput; 23 | this.key = key; 24 | } 25 | 26 | private PrintStream getDelegate() { 27 | PrintStream localCopy = delegate; 28 | if(localCopy==null) { 29 | localCopy = setDelegate(); 30 | } 31 | return localCopy==null ? ProbeManager.NULL_PRINT_STREAM : localCopy; 32 | } 33 | 34 | private synchronized PrintStream setDelegate() { 35 | delegate = namedOutput.get(key); 36 | return delegate; 37 | } 38 | 39 | private synchronized void clearDelegate() { 40 | delegate = null; 41 | } 42 | 43 | /** when closed, delegate is closed but stream can be reopened 44 | * next output will re-fetch from namedOutput map, 45 | * so change map value before closing this stream 46 | * to switch to a new output 47 | */ 48 | @Override 49 | public void close() { 50 | getDelegate().close(); 51 | clearDelegate(); 52 | } 53 | 54 | @Override public void write(byte[] b) throws IOException { getDelegate().write(b); } 55 | @Override public void flush() { getDelegate().flush(); } 56 | @Override public boolean checkError() { return getDelegate().checkError(); } 57 | @Override public void write(int b) { getDelegate().write(b); } 58 | @Override public void write(byte[] buf, int off, int len) { getDelegate().write(buf, off, len); } 59 | @Override public void print(boolean b) { getDelegate().print(b); } 60 | @Override public void print(char c) { getDelegate().print(c); } 61 | @Override public void print(int i) { getDelegate().print(i); } 62 | @Override public void print(long l) { getDelegate().print(l); } 63 | @Override public void print(float f) { getDelegate().print(f); } 64 | @Override public void print(double d) { getDelegate().print(d); } 65 | @Override public void print(char[] s) { getDelegate().print(s); } 66 | @Override public void print(String s) { getDelegate().print(s); } 67 | @Override public void print(Object obj) { getDelegate().print(obj); } 68 | @Override public void println() { getDelegate().println(); } 69 | @Override public void println(boolean x) { getDelegate().println(x); } 70 | @Override public void println(char x) { getDelegate().println(x); } 71 | @Override public void println(int x) { getDelegate().println(x); } 72 | @Override public void println(long x) { getDelegate().println(x); } 73 | @Override public void println(float x) { getDelegate().println(x); } 74 | @Override public void println(double x) { getDelegate().println(x); } 75 | @Override public void println(char[] x) { getDelegate().println(x); } 76 | @Override public void println(String x) { getDelegate().println(x); } 77 | @Override public void println(Object x) { getDelegate().println(x); } 78 | @Override public PrintStream printf(String format, Object... args) { return getDelegate().printf(format, args); } 79 | @Override public PrintStream printf(Locale l, String format, Object... args) { return getDelegate().printf(l, format, args); } 80 | @Override public PrintStream format(String format, Object... args) { return getDelegate().format(format, args); } 81 | @Override public PrintStream format(Locale l, String format, Object... args) { return getDelegate().format(l, format, args); } 82 | @Override public PrintStream append(CharSequence csq) { return getDelegate().append(csq); } 83 | @Override public PrintStream append(CharSequence csq, int start, int end) { return getDelegate().append(csq, start, end); } 84 | @Override public PrintStream append(char c) { return getDelegate().append(c); } 85 | } -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/com/dell/gumshoe/util/Output.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.util; 2 | 3 | import java.util.Properties; 4 | 5 | 6 | public class Output { 7 | private static boolean verbose; 8 | 9 | public static void setVerbose(boolean value) { verbose = value; } 10 | public static void configure(Configuration config) { 11 | setVerbose(config.isTrue("gumshoe.verbose", false)); 12 | } 13 | public static void configure(Properties p) { 14 | setVerbose(Boolean.valueOf(p.getProperty("gumshoe.verbose", "false"))); 15 | } 16 | 17 | ///// 18 | 19 | public static void print(String before, String... message) { 20 | final StringBuilder msg = new StringBuilder(); 21 | msg.append(before); 22 | for(String part : message) { 23 | msg.append(" ").append(part); 24 | } 25 | System.out.println(msg.toString()); 26 | } 27 | 28 | public static void error(Throwable t, String... message) { 29 | print("GUMSHOE ERROR!", message); 30 | t.printStackTrace(); 31 | } 32 | 33 | public static void error(String... message) { 34 | print("GUMSHOE ERROR!", message); 35 | } 36 | 37 | public static void debug(String... message) { 38 | if(verbose) { 39 | print("GUMSHOE:", message); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gumshoe-probes/src/main/java/sun/misc/IoTrace.java: -------------------------------------------------------------------------------- 1 | package sun.misc; 2 | 3 | import com.dell.gumshoe.hook.IoTraceHandler; 4 | 5 | import java.net.InetAddress; 6 | 7 | /** redefine template from rt.jar 8 | * 9 | * do as little work as possible here -- define a proper interface and way to set it, 10 | * then do any real work outside of hacking sun.misc package 11 | */ 12 | public final class IoTrace { 13 | private IoTrace() { } 14 | /** 15 | * Called before data is read from a socket. 16 | * 17 | * @return a context object 18 | */ 19 | public static Object socketReadBegin() { 20 | return IoTraceHandler.socketReadBegin(); 21 | } 22 | 23 | /** 24 | * Called after data is read from the socket. 25 | * 26 | * @param context 27 | * the context returned by the previous call to socketReadBegin() 28 | * @param address 29 | * the remote address the socket is bound to 30 | * @param port 31 | * the remote port the socket is bound to 32 | * @param timeout 33 | * the SO_TIMEOUT value of the socket (in milliseconds) or 0 if 34 | * there is no timeout set 35 | * @param bytesRead 36 | * the number of bytes read from the socket, 0 if there was an 37 | * error reading from the socket 38 | */ 39 | public static void socketReadEnd(Object context, InetAddress address, int port, int timeout, long bytesRead) { 40 | IoTraceHandler.socketReadEnd(context, address, port, timeout, bytesRead); 41 | } 42 | 43 | /** 44 | * Called before data is written to a socket. 45 | * 46 | * @return a context object 47 | */ 48 | public static Object socketWriteBegin() { 49 | return IoTraceHandler.socketWriteBegin(); 50 | } 51 | 52 | /** 53 | * Called after data is written to a socket. 54 | * 55 | * @param context 56 | * the context returned by the previous call to 57 | * socketWriteBegin() 58 | * @param address 59 | * the remote address the socket is bound to 60 | * @param port 61 | * the remote port the socket is bound to 62 | * @param bytesWritten 63 | * the number of bytes written to the socket, 0 if there was an 64 | * error writing to the socket 65 | */ 66 | public static void socketWriteEnd(Object context, InetAddress address, int port, long bytesWritten) { 67 | IoTraceHandler.socketWriteEnd(context, address, port, bytesWritten); 68 | } 69 | 70 | /** 71 | * Called before data is read from a file. 72 | * 73 | * @param path 74 | * the path of the file 75 | * @return a context object 76 | */ 77 | public static Object fileReadBegin(String path) { 78 | return IoTraceHandler.fileReadBegin(path); 79 | } 80 | 81 | /** 82 | * Called after data is read from a file. 83 | * 84 | * @param context 85 | * the context returned by the previous call to fileReadBegin() 86 | * @param bytesRead 87 | * the number of bytes written to the file, 0 if there was an 88 | * error writing to the file 89 | */ 90 | public static void fileReadEnd(Object context, long bytesRead) { 91 | IoTraceHandler.fileReadEnd(context, bytesRead); 92 | } 93 | 94 | /** 95 | * Called before data is written to a file. 96 | * 97 | * @param path 98 | * the path of the file 99 | * @return a context object 100 | */ 101 | public static Object fileWriteBegin(String path) { 102 | return IoTraceHandler.fileWriteBegin(path); 103 | } 104 | 105 | /** 106 | * Called after data is written to a file. 107 | * 108 | * @param context 109 | * the context returned by the previous call to fileReadBegin() 110 | * @param bytesWritten 111 | * the number of bytes written to the file, 0 if there was an 112 | * error writing to the file 113 | */ 114 | public static void fileWriteEnd(Object context, long bytesWritten) { 115 | IoTraceHandler.fileWriteEnd(context, bytesWritten); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /gumshoe-probes/src/test/java/com/dell/gumshoe/TestIOAccumulator.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.dell.gumshoe.network.AddressMatcher; 6 | import com.dell.gumshoe.network.MultiAddressMatcher; 7 | import com.dell.gumshoe.network.SocketIOAccumulator; 8 | import com.dell.gumshoe.network.SocketIODetailAdder; 9 | import com.dell.gumshoe.network.SocketIOMonitor; 10 | import com.dell.gumshoe.network.SubnetAddress; 11 | import com.dell.gumshoe.stack.Stack; 12 | import com.dell.gumshoe.stack.StackFilter; 13 | import com.dell.gumshoe.stack.StandardFilter; 14 | import com.dell.gumshoe.stats.ValueReporter; 15 | 16 | import org.junit.Ignore; 17 | import org.junit.Test; 18 | 19 | import java.net.InetAddress; 20 | import java.util.Map; 21 | 22 | public class TestIOAccumulator { 23 | int COUNT = 15; 24 | 25 | /** NOTE: can only run this test with project jarfile added to JVM bootclasspath */ 26 | @Ignore 27 | @Test 28 | public void testCollectsStatistics() throws Exception { 29 | AddressMatcher[] accept = { new SubnetAddress("127.0.0.1/32:1234") }; 30 | AddressMatcher[] reject = { new SubnetAddress("127.0.0.1/32:*") }; 31 | final MultiAddressMatcher socketFilter = new MultiAddressMatcher(accept, reject); 32 | 33 | SocketIOMonitor ioMonitor = new SocketIOMonitor(socketFilter, false, 500, Thread.MIN_PRIORITY, 1, false); 34 | StackFilter filter = StandardFilter.builder().withEndsOnly(1, 0).build(); 35 | SocketIOAccumulator ioAccumulator = new SocketIOAccumulator(filter); 36 | 37 | ValueReporter reporter = new ValueReporter("socket-io", ioAccumulator); 38 | ioMonitor.addListener(ioAccumulator); 39 | ioMonitor.initializeProbe(); 40 | 41 | InetAddress addr = InetAddress.getByName("www.yahoo.com"); 42 | InetAddress self = InetAddress.getLocalHost(); 43 | 44 | for(int j=0;j<3;j++) { 45 | for(int i=0;i stats = ioAccumulator.getStats(); 64 | assertEquals(3, stats.size()); 65 | ioAccumulator.reset(); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /gumshoe-probes/src/test/java/com/dell/gumshoe/TestSocketDiag.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe; 2 | 3 | import com.dell.gumshoe.network.SocketCloseMonitor; 4 | import com.dell.gumshoe.network.SocketCloseMonitor.SocketImplDecorator; 5 | import com.dell.gumshoe.network.SocketIOMonitor; 6 | import com.dell.gumshoe.stack.StackFilter; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | import java.net.ServerSocket; 12 | import java.net.Socket; 13 | import java.net.UnknownHostException; 14 | import java.util.List; 15 | 16 | import junit.framework.TestCase; 17 | 18 | /** create simple TCP client and server to test out the socket diag tools */ 19 | public class TestSocketDiag extends TestCase { 20 | static String testMessage = "now is the time for all good men to come to the aid of their king"; 21 | static byte[] testData = testMessage.getBytes(); 22 | 23 | SocketCloseMonitor target; 24 | 25 | @Override 26 | public void setUp() throws Exception { 27 | target = new SocketCloseMonitor(0, StackFilter.NONE); 28 | Socket.setSocketImplFactory(target); 29 | final SocketIOMonitor ioMonitor = new SocketIOMonitor(); 30 | // ioMonitor.addListener(new SocketIOMonitor.PrintEvents()); 31 | // IoTrace.setDelegate(ioMonitor); 32 | } 33 | 34 | public void testWithClientServer() throws Exception { 35 | // client just closes socket right away, but server will leave it open for 1sec 36 | EchoServer server = startServer(); 37 | executeClient(server); 38 | 39 | // see if we can find the open socket 40 | List sockets = target.findOpenedBefore(System.currentTimeMillis()); 41 | assertEquals(1, sockets.size()); 42 | 43 | // look in stack and see its from the server 44 | String stackLines = sockets.get(0).toString(); 45 | assertTrue(stackLines.contains(EchoServer.class.getSimpleName())); 46 | 47 | // TODO: use a better sync approach than sleep 48 | Thread.sleep(1500); 49 | 50 | // now server socket has closed, should be none left open 51 | sockets = target.findOpenedBefore(System.currentTimeMillis()); 52 | assertEquals(0, sockets.size()); 53 | } 54 | 55 | ///// 56 | 57 | private EchoServer startServer() { 58 | EchoServer server = new EchoServer(); 59 | new Thread(server).start(); 60 | return server; 61 | } 62 | 63 | private void executeClient(EchoServer server) throws InterruptedException, UnknownHostException, IOException { 64 | while(server.port==0) { 65 | Thread.sleep(100); 66 | } 67 | System.out.println("using port " + server.port); 68 | Socket socket = new Socket("127.0.0.1", server.port); 69 | 70 | InputStream in = socket.getInputStream(); 71 | OutputStream out = socket.getOutputStream(); 72 | 73 | System.out.println("client writing"); 74 | out.write(testData); 75 | out.flush(); 76 | socket.shutdownOutput(); 77 | System.out.println("client wrote"); 78 | 79 | StringBuilder buffer = new StringBuilder(); 80 | int inByte; 81 | while((inByte=in.read())!=-1) { 82 | buffer.append((char)inByte); 83 | } 84 | 85 | String received = buffer.toString(); 86 | System.out.println("client read: " + received); 87 | System.out.println("client closing"); 88 | socket.close(); 89 | } 90 | 91 | private static class EchoServer implements Runnable { 92 | private int port; 93 | private String received; 94 | private Exception thrown; 95 | @Override 96 | public void run() { 97 | try { 98 | runWithExceptions(); 99 | } catch(Exception e) { 100 | e.printStackTrace(); 101 | thrown = e; 102 | } 103 | 104 | } 105 | 106 | private void runWithExceptions() throws Exception { 107 | ServerSocket ss = new ServerSocket(0); 108 | port = ss.getLocalPort(); 109 | Socket socket = ss.accept(); 110 | 111 | InputStream in = socket.getInputStream(); 112 | OutputStream out = socket.getOutputStream(); 113 | 114 | System.out.println("server reading, avail = " + in.available()); 115 | StringBuilder buffer = new StringBuilder(); 116 | int inByte; 117 | while((inByte=in.read())==-1) { 118 | Thread.sleep(100); 119 | } 120 | buffer.append((char)inByte); 121 | while((inByte=in.read())!=-1) { 122 | buffer.append((char)inByte); 123 | } 124 | received = buffer.toString(); 125 | System.out.println("server read: " + received); 126 | 127 | out.write(received.getBytes()); 128 | socket.shutdownOutput(); 129 | System.out.println("server wrote"); 130 | 131 | Thread.sleep(1000); 132 | socket.close(); 133 | ss.close(); 134 | 135 | System.out.println("server closed"); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /gumshoe-probes/src/test/java/com/dell/gumshoe/file/TestPathMatcher.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.file; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class TestPathMatcher extends TestCase { 6 | private void assertMatches(String path, String pattern) { 7 | assertTrue(new PathPatternMatcher(pattern).matches(path)); 8 | } 9 | 10 | private void assertNoMatch(String path, String pattern) { 11 | assertFalse(new PathPatternMatcher(pattern).matches(path)); 12 | } 13 | 14 | private void assertInvalid(String pattern) { 15 | try { 16 | new PathPatternMatcher(pattern); 17 | fail("pattern should be invalid"); 18 | } catch(IllegalArgumentException e) { 19 | return; 20 | } 21 | } 22 | 23 | public void testFilenamePattern() { 24 | assertMatches("filename.txt", "*.txt"); 25 | assertNoMatch("filename.doc", "*.txt"); 26 | assertMatches("filename.txt", "f*n*t"); 27 | assertNoMatch("filename.doc", "f*n*t"); 28 | assertMatches("filename.txt", "*.*"); 29 | assertNoMatch("core", "*.*"); 30 | 31 | assertMatches("relative/filename.txt", "*.txt"); 32 | assertNoMatch("relative/filename.doc", "*.txt"); 33 | assertMatches("relative/filename.txt", "f*n*t"); 34 | assertNoMatch("relative/filename.doc", "f*n*t"); 35 | assertMatches("relative/filename.txt", "*.*"); 36 | assertNoMatch("relative/core", "*.*"); 37 | 38 | assertMatches("/absolute/filename.txt", "*.txt"); 39 | assertNoMatch("/absolute/filename.doc", "*.txt"); 40 | assertMatches("/absolute/filename.txt", "f*n*t"); 41 | assertNoMatch("/absolute/filename.doc", "f*n*t"); 42 | assertMatches("/absolute/filename.txt", "*.*"); 43 | assertNoMatch("/absolute/core", "*.*"); 44 | } 45 | 46 | public void testDirnamePattern() { 47 | assertMatches("some/dirname/file.txt", "some/dirname/*"); 48 | assertMatches("some/dirname/file.txt", "*/dirname/file.txt"); 49 | assertMatches("some/dirname/file.txt", "*/dirname/*"); 50 | assertMatches("some/dirname/file.txt", "**/dirname/**"); 51 | 52 | assertNoMatch("/absolute/some/dirname/file.txt", "some/dirname/*"); 53 | assertNoMatch("/absolute/some/dirname/file.txt", "*/dirname/file.txt"); 54 | assertNoMatch("/absolute/some/dirname/file.txt", "*/dirname/*"); 55 | assertMatches("/absolute/some/dirname/file.txt", "**/dirname/**"); 56 | 57 | assertMatches("relative/a/b/c/d/file.txt", "**/c/**"); 58 | assertMatches("relative/a/b/c/d/file.txt", "**/d/file.txt"); 59 | assertNoMatch("relative/a/b/c/d/file.txt", "**/c/file.txt"); 60 | assertNoMatch("relative/a/b/c/d/file.txt", "relative/**/c/*"); 61 | assertMatches("relative/a/b/c/d/file.txt", "relative/**/c/**"); 62 | assertMatches("relative/a/b/c/d/file.txt", "relative/**/b/**/d/**"); 63 | assertNoMatch("relative/a/b/c/d/file.txt", "relative/**/b/**/c/**"); 64 | assertNoMatch("relative/a/b/c/d/file.txt", "relative/**/d/**/b/**"); 65 | } 66 | 67 | public void testInvalid() { 68 | assertInvalid("some**thing"); 69 | assertInvalid("some/**thing"); 70 | assertInvalid("some**/thing"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /gumshoe-probes/src/test/java/com/dell/gumshoe/socket/TestParsing.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.socket; 2 | 3 | import com.dell.gumshoe.network.SocketEvent; 4 | import com.dell.gumshoe.network.SocketIODetailAdder; 5 | import com.dell.gumshoe.network.SocketReadEvent; 6 | import com.dell.gumshoe.network.SocketWriteEvent; 7 | import com.dell.gumshoe.stack.Stack; 8 | 9 | import java.net.InetAddress; 10 | import java.text.MessageFormat; 11 | import java.text.ParseException; 12 | 13 | import junit.framework.TestCase; 14 | 15 | public class TestParsing extends TestCase { 16 | public void testDetailParse() throws Exception { 17 | SocketEvent event1 = SocketReadEvent.begin(); 18 | SocketEvent event2 = SocketWriteEvent.begin(); 19 | Thread.sleep(100); 20 | event1.complete(InetAddress.getLocalHost(), 5678, 123123); 21 | Thread.sleep(100); 22 | event2.complete(InetAddress.getLocalHost(), 5678, 321321); 23 | SocketIODetailAdder orig = new SocketIODetailAdder(); 24 | orig.add(event1); 25 | orig.add(event2); 26 | 27 | String stringValue = orig.toString(); 28 | 29 | SocketIODetailAdder copy = SocketIODetailAdder.fromString(stringValue); 30 | assertEquals(copy.targets, orig.targets); 31 | assertEquals(copy.readBytes.get(), orig.readBytes.get()); 32 | assertEquals(copy.readCount.get(), orig.readCount.get()); 33 | assertEquals(copy.readTime.get(), orig.readTime.get()); 34 | assertEquals(1, orig.readCount.get()); 35 | assertEquals(copy.writeBytes.get(), orig.writeBytes.get()); 36 | assertEquals(copy.writeCount.get(), orig.writeCount.get()); 37 | assertEquals(copy.writeTime.get(), orig.writeTime.get()); 38 | assertEquals(1, orig.writeCount.get()); 39 | } 40 | 41 | public void testStackParse() { 42 | StackTraceElement orig = new StackTraceElement("foo", "bar", "baz", 123); 43 | String stringValue = Stack.FRAME_PREFIX + orig.toString(); 44 | StackTraceElement copy = Stack.parseFrame(stringValue); 45 | assertEquals(orig, copy); 46 | 47 | StackTraceElement report = Stack.parseFrame(" at com.enstratus.task.TaskBlocker.getTaskInfo(TaskBlocker.java:40)"); 48 | assertEquals("com.enstratus.task.TaskBlocker", report.getClassName()); 49 | assertEquals("getTaskInfo", report.getMethodName()); 50 | assertEquals(40, report.getLineNumber()); 51 | } 52 | 53 | public void testTagParse() throws ParseException { 54 | String line = ""; 55 | Object[] parts = new MessageFormat("").parse(line); 56 | String timePart = (String) parts[0]; 57 | assertEquals("2016-01-05 22:31:05", timePart); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /gumshoe-probes/src/test/java/com/dell/gumshoe/socket/TestSubnetMatcher.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.socket; 2 | 3 | import com.dell.gumshoe.network.SubnetAddress; 4 | 5 | import java.text.ParseException; 6 | 7 | import junit.framework.TestCase; 8 | 9 | public class TestSubnetMatcher extends TestCase { 10 | public void testParseValid() throws ParseException { 11 | String[] valid = { 12 | "127.0.0.1/24:1234", 13 | "127.0.0.1/20:1234", 14 | "127.0.0.1/8:1", 15 | "127.0.0.1/32:*", 16 | "255.255.255.0/0:0", 17 | "0.0.0.255/32:65535" 18 | }; 19 | 20 | byte b = (byte)255; 21 | int i=(int)b; 22 | System.out.println("255: " + i); 23 | for(String desc : valid) { 24 | System.out.println(desc + ": " + new SubnetAddress(desc).toString()); 25 | } 26 | } 27 | 28 | public void testMatch() throws Exception { 29 | assertTrue(new SubnetAddress("123.45.67.89/24:1234").matches(new byte[] { 123, 45, 67, 22 }, 1234)); 30 | assertFalse(new SubnetAddress("123.45.67.89/24:1234").matches(new byte[] { 123, 45, 77, 22 }, 1234)); 31 | assertFalse(new SubnetAddress("123.45.67.89/24:1234").matches(new byte[] { 123, 45, 67, 22 }, 1235)); 32 | assertTrue(new SubnetAddress("123.45.67.89/24:*").matches(new byte[] { 123, 45, 67, 22 }, 1235)); 33 | assertTrue(new SubnetAddress("123.45.67.89/20:1234").matches(new byte[] { 123, 45, 77, 22 }, 1234)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /gumshoe-probes/src/test/resources/gumshoe.properties: -------------------------------------------------------------------------------- 1 | 2 | ##### SOCKET IO 3 | 4 | gumshoe.socket-io.enabled=false 5 | gumshoe.socket-io.mbean=true 6 | gumshoe.socket-io.period= 7 | gumshoe.socket-io.exclude= 8 | gumshoe.socket-io.use-nio-hooks=true 9 | gumshoe.socket-io.handler.stats-enabled=true 10 | gumshoe.socket-io.filter.none=true 11 | -------------------------------------------------------------------------------- /gumshoe-probes/unused/IODetail.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.stats; 2 | 3 | import com.dell.gumshoe.io.IOEvent; 4 | import com.dell.gumshoe.socket.io.SocketIOMonitor; 5 | import com.dell.gumshoe.socket.io.SocketIOMonitor.RW; 6 | 7 | import java.net.InetAddress; 8 | 9 | /** thread-safety: toString and fromString are synchronized on FORMAT, 10 | * contention not likely in current usage so this is chosen 11 | * to reduce overhead of creating lots of new MessageFormat instances. 12 | * consider change if use-case is different. 13 | */ 14 | public class IODetail { 15 | private static String convertAddress(InetAddress addr, int port) { 16 | final byte[] ip = addr.getAddress(); 17 | return String.format("%d.%d.%d.%d:%d", 255&ip[0], 255&ip[1], 255&ip[2], 255&ip[3], port); 18 | } 19 | 20 | final String address; 21 | final long readBytes; 22 | final long readTime; 23 | final long writeBytes; 24 | final long writeTime; 25 | 26 | public IODetail(IOEvent e) { 27 | this(convertAddress(e.getAddress(), e.getPort()), e.getReadBytes(), e.getReadElapsed(), e.getWriteBytes(), e.getWriteElapsed()); 28 | } 29 | 30 | public IODetail(String address, long readBytes, long readTime, long writeBytes, long writeTime) { 31 | this.address = address; 32 | this.readBytes = readBytes; 33 | this.readTime = readTime; 34 | this.writeBytes = writeBytes; 35 | this.writeTime = writeTime; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return String.format("%s: r %d bytes in %d ms, w %d bytes in %d ms", 41 | address, readBytes, readTime, writeBytes, writeTime ); 42 | } 43 | } -------------------------------------------------------------------------------- /gumshoe-tools/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /gumshoe-tools/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | gumshoe-tools 6 | 7 | tools for analyzing gumshoe probe results 8 | 9 | 4.12 10 | 11 | 12 | 13 | 14 | junit 15 | junit 16 | ${junit.version} 17 | test 18 | 19 | 20 | com.dell 21 | gumshoe-probes 22 | 0.1.0-SNAPSHOT 23 | 24 | 25 | 26 | 27 | 28 | 29 | src/main/resources 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-compiler-plugin 36 | 3.3 37 | 38 | 1.7 39 | 1.7 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-surefire-plugin 45 | 46 | 47 | **/*Test*.* 48 | 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-assembly-plugin 54 | 55 | 56 | 57 | attached 58 | 59 | package 60 | 61 | 62 | jar-with-dependencies 63 | 64 | 65 | 66 | com.dell.gumshoe.inspector.Main 67 | 68 | 69 | inspector 70 | false 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | com.dell 79 | 0.1.0-SNAPSHOT 80 | Gumshoe Tools 81 | 82 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/Main.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector; 2 | 3 | import com.dell.gumshoe.Agent; 4 | import com.dell.gumshoe.ProbeManager; 5 | 6 | import javax.swing.JFrame; 7 | import javax.swing.UIManager; 8 | import javax.swing.UIManager.LookAndFeelInfo; 9 | import javax.swing.WindowConstants; 10 | 11 | import java.awt.Dimension; 12 | import java.awt.Toolkit; 13 | import java.lang.reflect.InvocationTargetException; 14 | import java.lang.reflect.Method; 15 | 16 | /** inspector main window and launcher 17 | * 18 | * running as an application, 19 | * this will start inspector GUI in a frame and then look for arguments. 20 | * first argument is the name of the target main class to run, 21 | * remaining arguments are passed as the args to that main. 22 | */ 23 | public class Main { 24 | private static final int DEFAULT_WIDTH_PERCENT = 70; 25 | private static final int DEFAULT_HEIGHT_PERCENT = 90; 26 | 27 | public static void main(String[] args) throws Throwable { 28 | final boolean hasTargetApp = args.length>0; 29 | if(hasTargetApp) { 30 | launchTargetApp(args); 31 | } 32 | launchGUI(hasTargetApp); 33 | } 34 | 35 | private static void launchGUI(boolean hasMain) throws Exception { 36 | try { 37 | for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { 38 | if ("Nimbus".equals(info.getName())) { 39 | UIManager.setLookAndFeel(info.getClassName()); 40 | break; 41 | } 42 | } 43 | } catch (Exception e) { 44 | // not available? will use default L&F 45 | } 46 | 47 | final boolean forceProbe = Boolean.getBoolean("gumshoe.probe.enabled"); 48 | final boolean needProbe = forceProbe || hasMain; 49 | 50 | final ProbeManager probe = needProbe ? ProbeManager.getInstance() : null; 51 | 52 | final JFrame frame = new JFrame("Inspector"); 53 | if( ! hasMain) { 54 | // if this is the only program, click X to exit (otherwise just hide) 55 | frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 56 | } 57 | 58 | frame.getContentPane().add(new GUI(frame, probe, hasMain)); 59 | frame.pack(); 60 | 61 | final Dimension fullScreenSize = Toolkit.getDefaultToolkit().getScreenSize(); 62 | final int height = fullScreenSize.height; 63 | final int width = fullScreenSize.width; 64 | final Dimension unmaximizedSize = new Dimension(DEFAULT_WIDTH_PERCENT*width/100, DEFAULT_HEIGHT_PERCENT*height/100); 65 | frame.setSize(unmaximizedSize); 66 | 67 | frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 68 | frame.setVisible(true); 69 | } 70 | 71 | private static void launchTargetApp(String[] args) throws Throwable { 72 | final String mainClassName = args[0]; 73 | final Class mainClass = Class.forName(mainClassName); 74 | final Method mainMethod = mainClass.getDeclaredMethod("main", String[].class); 75 | 76 | final String[] remainingArgs = new String[args.length-1]; 77 | if(args.length>1) { 78 | System.arraycopy(args, 1, remainingArgs, 0, args.length-1); 79 | } 80 | 81 | try { 82 | mainMethod.invoke(mainClass, new Object[] { remainingArgs }); 83 | } catch (InvocationTargetException e) { 84 | throw e.getCause(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/ReportSource.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector; 2 | 3 | import com.dell.gumshoe.inspector.tools.ReportSelectionListener; 4 | 5 | public interface ReportSource { 6 | public void addListener(ReportSelectionListener listener); 7 | public boolean hasNext(); 8 | public boolean hasPrevious(); 9 | public void nextReport(); 10 | public void previousReport(); 11 | } 12 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/graph/Box.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.graph; 2 | 3 | import com.dell.gumshoe.inspector.helper.DataTypeHelper; 4 | 5 | import java.awt.Color; 6 | import java.awt.Graphics; 7 | import java.awt.Rectangle; 8 | 9 | /** cached model for individual display rectangles each showing details of a stack frame */ 10 | class Box { 11 | public static final long RESOLUTION = 10000; 12 | 13 | private final int row; 14 | private float position, width; 15 | private final StackFrameNode boxNode; 16 | private final StackFrameNode parentNode; 17 | private String label; 18 | 19 | public Box(int row, float position, float width, StackFrameNode boxNode, StackFrameNode parentNode) { 20 | this.row = row; 21 | this.position = position; 22 | this.width = width; 23 | this.boxNode = boxNode; 24 | this.parentNode = parentNode; 25 | } 26 | 27 | public StackTraceElement getFrame() { return boxNode.getFrame(); } 28 | public float getPosition() { return position; } 29 | public float getWidth() { return width; } 30 | public StackFrameNode getNode() { return boxNode; } 31 | public StackFrameNode getParentNode() { return parentNode; } 32 | 33 | public Rectangle getBounds(float rowHeight, int displayWidth, int rowsMinusOne, DisplayOptions o) { 34 | final int boxX = (int) (position * displayWidth); 35 | final int boxWidth = (int)(width * displayWidth); 36 | final int boxY = (int) (rowHeight * (o.byCalled?(rowsMinusOne-row):row)); 37 | final int boxHeight = (int)rowHeight; 38 | return new Rectangle(boxX, boxY, boxWidth, boxHeight); 39 | } 40 | 41 | public void draw(Graphics g, float rowHeight, int displayWidth, int rowsMinusOne, DisplayOptions o, Box selected) { 42 | final int boxX = (int) (position * displayWidth); 43 | final int boxWidth = (int)(width * displayWidth); 44 | final int boxY = (int) (rowHeight * (o.byCalled?(rowsMinusOne-row):row)); 45 | final int boxHeight = (int)rowHeight; 46 | final boolean isSelected = this==selected; 47 | g.setClip(boxX, boxY, boxWidth+1, boxHeight+1); 48 | 49 | final Color baseColor = getColor(o); 50 | final Color color = isSelected ? baseColor.darker() : baseColor; 51 | 52 | g.setColor(color); 53 | g.fillRect(boxX, boxY, boxWidth, boxHeight); 54 | g.setColor(Color.BLACK); 55 | g.drawRect(boxX, boxY, boxWidth, boxHeight); 56 | g.drawString(getLabelText(), boxX+1, boxY+15); 57 | g.setClip(null); 58 | } 59 | 60 | public boolean contains(float rowHeight, int displayWidth, int rows, long total, DisplayOptions o, int x, int y) { 61 | // same box coordinates as draw(), but short circuit if possible 62 | final int boxX = (int) (position * displayWidth); 63 | if(x=boxX+boxWidth) { return false; } 67 | 68 | final int boxY = (int) (rowHeight * (o.byCalled?(rows-row-1):row)); 69 | if(y=boxY+boxHeight) { return false; } 73 | 74 | return true; 75 | } 76 | 77 | private Color getColor(DisplayOptions o) { 78 | // (50%,100%]--> 0, (33%,50%]-->1, (25%,33%]-->2,... (1/N,1/(N-1)]-->(N-2) 79 | int index = (int) (1f/width)-1; 80 | index = Math.min(Math.max(0, index), StackGraphPanel.BOX_COLORS.length-1); 81 | return StackGraphPanel.BOX_COLORS[index]; 82 | } 83 | 84 | public String getLabelText() { 85 | if(label==null) { 86 | final String[] parts = boxNode.getFrame().getClassName().split("\\."); 87 | final StringBuilder labelText = new StringBuilder(parts[parts.length-1]); 88 | 89 | // the rest may be missing due to simplification filter 90 | final String methodName = boxNode.getFrame().getMethodName(); 91 | if(methodName.length()>0) { 92 | labelText.append(".").append(methodName); 93 | } 94 | final int lineNumber = boxNode.getFrame().getLineNumber(); 95 | if(lineNumber>0) { 96 | labelText.append(":").append(lineNumber); 97 | } 98 | label = labelText.toString(); 99 | } 100 | return label; 101 | } 102 | 103 | public String getToolTipText() { 104 | return getHelper().getToolTipText(boxNode, parentNode); 105 | } 106 | 107 | public String getDetailText() { 108 | return getHelper().getDetailText(boxNode, parentNode); 109 | } 110 | 111 | private DataTypeHelper getHelper() { 112 | return DataTypeHelper.forType(boxNode.getDetail().getType()); 113 | } 114 | 115 | @Override 116 | public String toString() { 117 | return String.format("%3d %.2f %.2f %s", row, position, width, boxNode.getFrame().toString()); 118 | } 119 | } -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/graph/DisplayOptions.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.graph; 2 | 3 | 4 | /** configurable aspects of rendering frames and boxes */ 5 | public class DisplayOptions { 6 | public enum WidthScale { VALUE, LOG_VALUE, EQUAL } 7 | public enum Order { BY_VALUE, BY_NAME } 8 | final boolean byCalled; // true=flame graph; false=root graph 9 | // final IODirection direction; 10 | // final IOUnit unit; 11 | final Order order; 12 | final WidthScale scale; 13 | final float minPercent; 14 | 15 | public DisplayOptions() { 16 | this(false, Order.BY_NAME, WidthScale.VALUE, 0f); 17 | } 18 | 19 | public DisplayOptions(boolean byCalled, Order order, WidthScale scale, float minPercent) { 20 | this.byCalled = byCalled; 21 | this.order = order; 22 | this.scale = scale; 23 | this.minPercent = minPercent; 24 | } 25 | } -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/graph/Ruler.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.graph; 2 | 3 | import javax.swing.JLabel; 4 | import javax.swing.JScrollPane; 5 | 6 | import java.awt.Color; 7 | import java.awt.Dimension; 8 | import java.awt.Graphics; 9 | 10 | public class Ruler extends JLabel { 11 | // pixel sizes 12 | public static final int RULER_HEIGHT = 20; 13 | private static final int RULER_MAJOR_HEIGHT = 20; 14 | private static final int RULER_MINOR_HEIGHT = 10; 15 | 16 | // number of major and minor divisions 17 | private static final int RULER_MAJOR = 4; 18 | private static final int RULER_MINOR = 20; 19 | 20 | private final JScrollPane scrollPane; 21 | public Ruler(JScrollPane scrollPane) { 22 | this.scrollPane = scrollPane; 23 | setBackground(Color.WHITE); 24 | setOpaque(true); 25 | } 26 | 27 | public Dimension getPreferredSize() { 28 | final Dimension size = scrollPane.getViewport().getView().getSize(); 29 | size.height = RULER_HEIGHT; 30 | return size; 31 | } 32 | 33 | public void paintComponent(Graphics g) { 34 | super.paintComponent(g); 35 | 36 | final int width = getSize().width; // 100% 37 | final int height = getSize().height; 38 | g.setColor(Color.BLACK); 39 | g.drawRect(0, 0, width, height); 40 | for(int i=1;i data); 25 | public abstract JComponent getOptionEditor(); 26 | public abstract JComponent getSelectionComponent(); 27 | public abstract boolean isSelected(); 28 | public abstract void addListener(ProbeManager probe, Listener listener); 29 | public abstract long getStatValue(StatisticAdder composite); 30 | 31 | protected JPanel getDisclaimer() { 32 | return flow(new JLabel("** = these stats are not additive; widths from different stacks are not comparable")); 33 | } 34 | 35 | protected String pct(Number num, Number num2, Number div, Number div2) { 36 | return pct(num.longValue()+num2.longValue(), div.longValue()+div2.longValue()); 37 | } 38 | 39 | protected String pct(Number num, Number div) { 40 | if(div.longValue()==0) { return ""; } 41 | return " " + (100*num.longValue()/div.longValue()) + "%"; 42 | } 43 | 44 | protected long div(Number num, Number den) { 45 | return den.longValue()==0 ? num.longValue() : Math.round(num.floatValue()/den.floatValue()); 46 | } 47 | 48 | protected String getFrames(Collection frames) { 49 | final StringBuilder out = new StringBuilder(); 50 | for(StackTraceElement frame : frames) { 51 | out.append("\n ").append(frame); 52 | } 53 | return out.toString(); 54 | } 55 | 56 | public String getDetailText(StackFrameNode boxNode, StackFrameNode parentNode) { 57 | final StringBuilder msg = new StringBuilder(); 58 | msg.append(String.format("Frame: %s\n\nContext:\n", boxNode.getFrame())); 59 | boxNode.appendContext(msg); 60 | msg.append("\n"); 61 | 62 | msg.append(getStatDetails(boxNode.getDetail(), parentNode.getDetail())); 63 | 64 | final Set callingFrames = boxNode.getCallingFrames(); 65 | final Set calledFrames = boxNode.getCalledFrames(); 66 | msg.append(String.format("Calls %d methods: %s\n\nCalled by %d methods: %s", 67 | calledFrames.size(), getFrames(calledFrames), 68 | callingFrames.size(), getFrames(callingFrames) )); 69 | 70 | return msg.toString(); 71 | } 72 | 73 | ///// 74 | 75 | private static final Map impl = new LinkedHashMap<>(); 76 | 77 | private static Map getImpl() { 78 | if(impl.isEmpty()) { 79 | impl.put(ProbeManager.CPU_USAGE_LABEL, new CPUUsageHelper()); 80 | impl.put(ProbeManager.SOCKET_IO_LABEL, new SocketIOHelper()); 81 | impl.put(ProbeManager.DATAGRAM_IO_LABEL, new DatagramIOHelper()); 82 | impl.put(ProbeManager.FILE_IO_LABEL, new FileIOHelper()); 83 | impl.put(ProbeManager.UNCLOSED_SOCKET_LABEL, new UnclosedSocketHelper()); 84 | } 85 | return impl; 86 | } 87 | 88 | public static Collection getTypes() { return getImpl().keySet(); } 89 | 90 | public static DataTypeHelper forType(String type) { 91 | return getImpl().get(type); 92 | } 93 | 94 | public static long getValue(StatisticAdder composite) { 95 | return forType(composite.getType()).getStatValue(composite); 96 | } 97 | 98 | public interface TypeDisplayOptions { } 99 | 100 | public static String getStatInfo(StatisticAdder target, StatisticAdder parent) { 101 | return forType(target.getType()).getStatDetails(target, parent); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/helper/DatagramIOHelper.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.helper; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.network.DatagramIODetailAdder; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StatisticAdder; 7 | import com.dell.gumshoe.stats.ValueReporter; 8 | import com.dell.gumshoe.stats.ValueReporter.Listener; 9 | 10 | import javax.swing.JCheckBox; 11 | import javax.swing.JComponent; 12 | 13 | import java.text.ParseException; 14 | 15 | public class DatagramIOHelper extends IOHelper { 16 | private final JCheckBox accept = new JCheckBox("datagram IO", true); 17 | 18 | @Override 19 | public JComponent getSelectionComponent() { return accept; } 20 | 21 | @Override 22 | public boolean isSelected() { return accept.isSelected(); } 23 | 24 | @Override 25 | public StatisticAdder parse(String value) throws ParseException { 26 | return DatagramIODetailAdder.fromString(value); 27 | } 28 | 29 | @Override 30 | public void addListener(ProbeManager probe, Listener listener) { 31 | final ValueReporter reporter = probe.getDatagramIOReporter(); 32 | if(reporter!=null) { 33 | reporter.addListener(listener); 34 | } 35 | } 36 | 37 | @Override 38 | protected String getTargetName() { 39 | return "addresses"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/helper/FileIOHelper.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.helper; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.file.FileIODetailAdder; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StatisticAdder; 7 | import com.dell.gumshoe.stats.ValueReporter; 8 | import com.dell.gumshoe.stats.ValueReporter.Listener; 9 | 10 | import javax.swing.JCheckBox; 11 | import javax.swing.JComponent; 12 | 13 | import java.text.ParseException; 14 | 15 | public class FileIOHelper extends IOHelper { 16 | private final JCheckBox acceptFileIO = new JCheckBox("file IO", true); 17 | 18 | @Override 19 | public JComponent getSelectionComponent() { return acceptFileIO; } 20 | 21 | @Override 22 | public boolean isSelected() { return acceptFileIO.isSelected(); } 23 | 24 | @Override 25 | public StatisticAdder parse(String value) throws ParseException { 26 | return FileIODetailAdder.fromString(value); 27 | } 28 | 29 | @Override 30 | public void addListener(ProbeManager probe, Listener listener) { 31 | final ValueReporter reporter = probe.getFileIOReporter(); 32 | if(reporter!=null) { 33 | reporter.addListener(listener); 34 | } 35 | } 36 | 37 | @Override 38 | protected String getTargetName() { 39 | return "files"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/helper/SocketIOHelper.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.helper; 2 | 3 | import com.dell.gumshoe.ProbeManager; 4 | import com.dell.gumshoe.network.SocketIODetailAdder; 5 | import com.dell.gumshoe.stats.IODetailAdder; 6 | import com.dell.gumshoe.stats.StatisticAdder; 7 | import com.dell.gumshoe.stats.ValueReporter; 8 | import com.dell.gumshoe.stats.ValueReporter.Listener; 9 | 10 | import javax.swing.JCheckBox; 11 | import javax.swing.JComponent; 12 | 13 | import java.text.ParseException; 14 | 15 | public class SocketIOHelper extends IOHelper { 16 | 17 | private final JCheckBox acceptSocketIO = new JCheckBox("socket IO", true); 18 | 19 | @Override 20 | public JComponent getSelectionComponent() { return acceptSocketIO; } 21 | 22 | @Override 23 | public boolean isSelected() { return acceptSocketIO.isSelected(); } 24 | 25 | @Override 26 | public StatisticAdder parse(String value) throws ParseException { 27 | return SocketIODetailAdder.fromString(value); 28 | } 29 | 30 | @Override 31 | public void addListener(ProbeManager probe, Listener listener) { 32 | final ValueReporter reporter = probe.getSocketIOReporter(); 33 | if(reporter!=null) { 34 | reporter.addListener(listener); 35 | } 36 | } 37 | 38 | @Override 39 | protected String getTargetName() { 40 | return "addresses"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/helper/UnclosedSocketHelper.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.helper; 2 | 3 | import static com.dell.gumshoe.util.Swing.flow; 4 | import static com.dell.gumshoe.util.Swing.groupButtons; 5 | import static com.dell.gumshoe.util.Swing.stackNorth; 6 | 7 | import com.dell.gumshoe.ProbeManager; 8 | import com.dell.gumshoe.inspector.graph.StackFrameNode; 9 | import com.dell.gumshoe.network.UnclosedStats; 10 | import com.dell.gumshoe.stack.Stack; 11 | import com.dell.gumshoe.stats.StatisticAdder; 12 | import com.dell.gumshoe.stats.ValueReporter; 13 | import com.dell.gumshoe.stats.ValueReporter.Listener; 14 | 15 | import javax.swing.JCheckBox; 16 | import javax.swing.JComponent; 17 | import javax.swing.JLabel; 18 | import javax.swing.JRadioButton; 19 | 20 | import java.text.ParseException; 21 | import java.util.Map; 22 | 23 | public class UnclosedSocketHelper extends DataTypeHelper { 24 | private final JRadioButton countStat = new JRadioButton("count", true); 25 | private final JRadioButton ageStat = new JRadioButton("age **"); 26 | private final JCheckBox acceptSocketUnclosed = new JCheckBox("unclosed sockets"); 27 | 28 | @Override 29 | public JComponent getSelectionComponent() { return acceptSocketUnclosed; } 30 | 31 | @Override 32 | public boolean isSelected() { return acceptSocketUnclosed.isSelected(); } 33 | 34 | @Override 35 | public String getToolTipText(StackFrameNode boxNode, StackFrameNode parentNode) { 36 | final UnclosedStats boxDetail = (UnclosedStats)boxNode.getDetail(); 37 | final UnclosedStats parentDetail = (UnclosedStats)parentNode.getDetail(); 38 | return String.format("\n" 39 | + "%s
\n" 40 | + "%d sockets%s opened up to %d ms ago\n" 41 | + "", 42 | 43 | boxNode.getFrame(), 44 | boxDetail.getCount(), pct(boxDetail.getCount(), parentDetail.getCount()), 45 | boxDetail.getMaxAge() ); 46 | } 47 | 48 | @Override 49 | public String getStatDetails(StatisticAdder nodeValue, StatisticAdder parentValue) { 50 | final UnclosedStats boxDetail = (UnclosedStats)nodeValue; 51 | final UnclosedStats parentDetail = (UnclosedStats)parentValue; 52 | return String.format( 53 | "Count:\n" 54 | + "%d open sockets%s\n\n" 55 | + "Age:\n" 56 | + "Up to %d ms\n\n", 57 | boxDetail.getCount(), pct(boxDetail.getCount(), parentDetail.getCount()), 58 | boxDetail.getMaxAge() ); 59 | } 60 | 61 | @Override 62 | public StatisticAdder parse(String value) throws ParseException { 63 | return UnclosedStats.fromString(value); 64 | } 65 | 66 | @Override 67 | public String getSummary(Map data) { 68 | UnclosedStats tally = new UnclosedStats(); 69 | for(StatisticAdder item : data.values()) { 70 | tally.add((UnclosedStats)item); 71 | } 72 | return data.size() + " entries, total " + tally; 73 | } 74 | 75 | @Override 76 | public JComponent getOptionEditor() { 77 | groupButtons(countStat, ageStat); 78 | return stackNorth(flow(new JLabel("Value: "), countStat, ageStat), getDisclaimer()); 79 | } 80 | 81 | @Override 82 | public long getStatValue(StatisticAdder composite) { 83 | final UnclosedStats value = (UnclosedStats) composite; 84 | return countStat.isSelected() ? value.getCount() : value.getMaxAge(); 85 | } 86 | 87 | @Override 88 | public void addListener(ProbeManager probe, Listener listener) { 89 | final ValueReporter reporter = probe.getUnclosedReporter(); 90 | if(reporter!=null) { 91 | reporter.addListener(listener); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/tools/AboutPanel.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.tools; 2 | 3 | import static com.dell.gumshoe.util.Swing.flow; 4 | import static com.dell.gumshoe.util.Swing.stackIn; 5 | import static com.dell.gumshoe.util.Swing.stackNorth; 6 | 7 | import javax.swing.BorderFactory; 8 | import javax.swing.JButton; 9 | import javax.swing.JLabel; 10 | import javax.swing.JPanel; 11 | import javax.swing.SwingConstants; 12 | 13 | import java.awt.BorderLayout; 14 | import java.awt.Font; 15 | import java.awt.event.ActionListener; 16 | 17 | public class AboutPanel extends JPanel implements HasCloseButton { 18 | private final JButton ok = new JButton("OK"); 19 | 20 | public AboutPanel() { 21 | super(new BorderLayout()); 22 | 23 | final Font bold = getFont().deriveFont(Font.BOLD); 24 | final JLabel gumshoe = new JLabel("Gumshoe"); 25 | final JLabel investigator = new JLabel("Inspector"); 26 | gumshoe.setFont(bold); 27 | investigator.setFont(bold); 28 | 29 | stackIn(this, BorderLayout.NORTH, 30 | flow(investigator, 31 | new JLabel("GUI for viewing Gumshoe results")), 32 | flow(new JLabel("is part of")), 33 | flow(gumshoe, 34 | new JLabel("resource utilization and performance analysis tools")), 35 | flow(new JLabel("(c) 2016 Dell, Inc.")), 36 | flow(new JLabel("Use allowed under terms of Apache License 2.0.")), 37 | flow(new JLabel("")), 38 | stackNorth(new JLabel("Thanks for the encouragement and support from the project managers", SwingConstants.LEFT)), 39 | stackNorth(new JLabel("at Dell Software Group who saw the value and set aside the time", SwingConstants.LEFT)), 40 | stackNorth(new JLabel("to develop this tool and share it with our customers and community.", SwingConstants.LEFT)), 41 | flow(new JLabel("")), 42 | flow(ok) ); 43 | setBorder(BorderFactory.createEmptyBorder(10,15,7,15)); 44 | } 45 | 46 | @Override 47 | public void addCloseListener(ActionListener listener) { 48 | ok.addActionListener(listener); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/tools/DetailPanel.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.tools; 2 | 3 | import static com.dell.gumshoe.util.Swing.flow; 4 | import static com.dell.gumshoe.util.Swing.stackNorth; 5 | 6 | import com.dell.gumshoe.inspector.graph.StackFrameNode; 7 | import com.dell.gumshoe.inspector.graph.StackGraphPanel; 8 | 9 | import javax.swing.BorderFactory; 10 | import javax.swing.DefaultListModel; 11 | import javax.swing.JButton; 12 | import javax.swing.JLabel; 13 | import javax.swing.JList; 14 | import javax.swing.JPanel; 15 | import javax.swing.JScrollPane; 16 | import javax.swing.JTextArea; 17 | import javax.swing.event.ListSelectionEvent; 18 | import javax.swing.event.ListSelectionListener; 19 | 20 | import java.awt.BorderLayout; 21 | import java.awt.event.ActionListener; 22 | import java.util.Set; 23 | 24 | public class DetailPanel extends JPanel implements HasCloseButton, ListSelectionListener { 25 | private static final String PROTOTYPE_FRAME = "com.dell.gumshoe.thread.ThreadMonitor$ThreadDumper.performDumpAndReschedule(ThreadMonitor.java:248)"; 26 | private final JLabel frame = new JLabel(); 27 | private final JLabel callType = new JLabel("Call:"); 28 | private final JTextArea stats = new JTextArea(); 29 | final JButton close = new JButton("Close"); 30 | 31 | final DefaultListModel callModel = new DefaultListModel(); 32 | final DefaultListModel contextModel = new DefaultListModel(); 33 | 34 | public DetailPanel() { 35 | super(new BorderLayout()); 36 | 37 | final JList contextList = new JList(contextModel); 38 | contextList.setVisibleRowCount(8); 39 | contextList.setPrototypeCellValue(PROTOTYPE_FRAME); 40 | final JList callList = new JList(callModel); 41 | callList.setVisibleRowCount(5); 42 | callList.setPrototypeCellValue(PROTOTYPE_FRAME); 43 | 44 | final JPanel frameRow = new JPanel(new BorderLayout()); 45 | frameRow.add(new JLabel("Stack frame:"), BorderLayout.WEST); 46 | frameRow.add(frame, BorderLayout.CENTER); 47 | 48 | final JPanel frameInfo = stackNorth( 49 | frameRow, 50 | new JLabel(" "), 51 | new JLabel("Context:"), new JScrollPane(contextList), 52 | new JLabel(), 53 | callType, new JScrollPane(callList), 54 | new JLabel(" "), 55 | new JLabel("Probe stats:")); 56 | frameInfo.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 57 | 58 | stats.setRows(15); 59 | add(frameInfo, BorderLayout.NORTH); 60 | add(stats, BorderLayout.CENTER); 61 | add(flow(close), BorderLayout.SOUTH); 62 | } 63 | 64 | public void setModel(StackFrameNode boxNode, StackFrameNode parentNode, String statInfo) { 65 | frame.setText(boxNode.getFrame().toString()); 66 | 67 | contextModel.clear(); 68 | for(StackTraceElement frame : boxNode.getContext()) { 69 | contextModel.addElement(frame); 70 | } 71 | 72 | final boolean byCalled = boxNode.isByCalled(); 73 | callType.setText(byCalled ? "Calls into:" : "Called by:"); 74 | final Set callFrames = byCalled ? boxNode.getCalledFrames() : boxNode.getCallingFrames(); 75 | callModel.clear(); 76 | for(StackTraceElement frame : callFrames) { 77 | callModel.addElement(frame); 78 | } 79 | 80 | stats.setText(statInfo); 81 | } 82 | 83 | @Override 84 | public void addCloseListener(ActionListener listener) { 85 | close.addActionListener(listener); 86 | } 87 | 88 | @Override 89 | public void valueChanged(ListSelectionEvent e) { 90 | final StackGraphPanel graph = (StackGraphPanel) e.getSource(); 91 | final StackFrameNode boxNode = graph.getSelection(); 92 | final StackFrameNode parentNode = graph.getSelectionParent(); 93 | final String statInfo = boxNode.getStats(parentNode); 94 | setModel(boxNode, parentNode, statInfo); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/tools/HasCloseButton.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.tools; 2 | 3 | import java.awt.event.ActionListener; 4 | 5 | public interface HasCloseButton { 6 | public void addCloseListener(ActionListener listener); 7 | } 8 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/tools/OptionEditor.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.tools; 2 | 3 | import static com.dell.gumshoe.util.Swing.columns; 4 | import static com.dell.gumshoe.util.Swing.flow; 5 | import static com.dell.gumshoe.util.Swing.groupButtons; 6 | import static com.dell.gumshoe.util.Swing.stackNorth; 7 | import static com.dell.gumshoe.util.Swing.stackSouth; 8 | import static com.dell.gumshoe.util.Swing.stackWest; 9 | import static com.dell.gumshoe.util.Swing.titled; 10 | 11 | import com.dell.gumshoe.inspector.graph.DisplayOptions; 12 | 13 | import javax.swing.JButton; 14 | import javax.swing.JCheckBox; 15 | import javax.swing.JLabel; 16 | import javax.swing.JPanel; 17 | import javax.swing.JRadioButton; 18 | import javax.swing.JTextField; 19 | 20 | import java.awt.BorderLayout; 21 | import java.awt.event.ActionListener; 22 | 23 | public class OptionEditor extends JPanel implements HasCloseButton { 24 | private final JRadioButton byCaller = new JRadioButton("show callers (root graph)", true); 25 | private final JRadioButton byCalled = new JRadioButton("show called methods (flame graph)"); 26 | private final JRadioButton valueWidth = new JRadioButton("statistic value", true); 27 | private final JRadioButton logWidth = new JRadioButton("log(value)"); 28 | private final JRadioButton equalWidth = new JRadioButton("equal width"); 29 | private final JCheckBox byValue = new JCheckBox("arrange by statistic value (left to right)"); 30 | private final JTextField statLimit = new JTextField(); 31 | private final JButton apply = new JButton("OK"); 32 | 33 | public OptionEditor() { 34 | groupButtons(byCalled, byCaller); 35 | final JPanel directionPanel = columns(new JLabel("Direction: "), byCaller, byCalled, new JLabel("")); 36 | 37 | groupButtons(valueWidth, logWidth, equalWidth); 38 | final JPanel widthPanel = columns(new JLabel("Cell width: "), valueWidth, logWidth, equalWidth); 39 | 40 | 41 | statLimit.setColumns(3); 42 | final JPanel displaySettingsPanel = columns( 43 | stackWest(new JLabel("Drop frames less than"), statLimit, new JLabel("%")), 44 | byValue); 45 | 46 | final JPanel graphPanel = titled("Graph generation options", 47 | stackNorth(directionPanel, widthPanel, displaySettingsPanel)); 48 | 49 | ///// 50 | 51 | final JPanel bottomPanel = stackSouth(flow(apply), graphPanel); 52 | 53 | setLayout(new BorderLayout()); 54 | add(bottomPanel, BorderLayout.SOUTH); 55 | } 56 | 57 | public void addActionListener(ActionListener listener) { 58 | apply.addActionListener(listener); 59 | } 60 | 61 | public DisplayOptions getOptions() { 62 | final DisplayOptions.Order order = byValue.isSelected() ? DisplayOptions.Order.BY_VALUE : DisplayOptions.Order.BY_NAME; 63 | 64 | final DisplayOptions.WidthScale width; 65 | if(valueWidth.isSelected()) width = DisplayOptions.WidthScale.VALUE; 66 | else if(logWidth.isSelected()) width = DisplayOptions.WidthScale.LOG_VALUE; 67 | else width = DisplayOptions.WidthScale.EQUAL; 68 | 69 | final boolean isInverted = byCalled.isSelected(); 70 | float minPct = 0f; 71 | try { 72 | minPct = Float.parseFloat(statLimit.getText()); 73 | } catch(Exception ignore) { 74 | 75 | } 76 | return new DisplayOptions(isInverted, order, width, minPct); 77 | } 78 | 79 | @Override 80 | public void addCloseListener(ActionListener listener) { 81 | apply.addActionListener(listener); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/tools/ReportSelectionListener.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.tools; 2 | 3 | import com.dell.gumshoe.inspector.ReportSource; 4 | import com.dell.gumshoe.stack.Stack; 5 | import com.dell.gumshoe.stats.StatisticAdder; 6 | 7 | import java.util.Map; 8 | 9 | public interface ReportSelectionListener { 10 | public void reportWasSelected(Object source, String time, String type, Map data); 11 | public void contentsChanged(ReportSource source); 12 | } -------------------------------------------------------------------------------- /gumshoe-tools/src/main/java/com/dell/gumshoe/inspector/tools/StatisticChooser.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.inspector.tools; 2 | 3 | import static com.dell.gumshoe.util.Swing.flow; 4 | import static com.dell.gumshoe.util.Swing.stackIn; 5 | import static com.dell.gumshoe.util.Swing.stackWest; 6 | 7 | import com.dell.gumshoe.inspector.ReportSource; 8 | import com.dell.gumshoe.inspector.graph.StackGraphPanel; 9 | import com.dell.gumshoe.inspector.helper.DataTypeHelper; 10 | import com.dell.gumshoe.stack.Stack; 11 | import com.dell.gumshoe.stats.StatisticAdder; 12 | 13 | import javax.swing.BorderFactory; 14 | import javax.swing.JButton; 15 | import javax.swing.JComboBox; 16 | import javax.swing.JLabel; 17 | import javax.swing.JPanel; 18 | 19 | import java.awt.BorderLayout; 20 | import java.awt.CardLayout; 21 | import java.awt.event.ActionEvent; 22 | import java.awt.event.ActionListener; 23 | import java.util.Map; 24 | 25 | public class StatisticChooser extends JPanel implements ReportSelectionListener, HasCloseButton { 26 | private final JComboBox statSelector = new JComboBox(DataTypeHelper.getTypes().toArray()); 27 | private final CardLayout statCard = new CardLayout(); 28 | private final JPanel statOptions = new JPanel(); 29 | private String lastLoadedType; 30 | private boolean updateWhenLoaded = true; 31 | private JButton close = new JButton("OK"); 32 | private StackGraphPanel graph; 33 | 34 | public StatisticChooser(StackGraphPanel graph) { 35 | super(new BorderLayout()); 36 | 37 | this.graph = graph; 38 | statSelector.addActionListener(new ActionListener() { 39 | @Override 40 | public void actionPerformed(ActionEvent e) { 41 | final String label = (String)statSelector.getSelectedItem(); 42 | statCard.show(statOptions, label); 43 | // if user manually selects current displayed type, 44 | // keep them in sync if new type is loaded 45 | // otherwise leave it on user's selected type 46 | // so viewing stats live won't change dropdown while user choosing a stat 47 | updateWhenLoaded = label.equals(lastLoadedType); 48 | } 49 | }); 50 | final JPanel statChooserPanel = stackWest(new JLabel("Select statistic for report type "), statSelector, new JLabel(":")); 51 | statOptions.setBorder(BorderFactory.createEmptyBorder(5, 10, 10, 10)); 52 | 53 | statOptions.setLayout(statCard); 54 | for(String typeName : DataTypeHelper.getTypes()) { 55 | statOptions.add(DataTypeHelper.forType(typeName).getOptionEditor(), typeName); 56 | } 57 | 58 | stackIn(this, BorderLayout.NORTH, statChooserPanel, statOptions, flow(close)); 59 | } 60 | 61 | @Override 62 | public void reportWasSelected(Object source, String time, String type, Map data) { 63 | lastLoadedType = type; 64 | if( ! isShowing()) { updateWhenLoaded = true; } 65 | if(updateWhenLoaded) { 66 | statSelector.setSelectedItem(type); 67 | } 68 | } 69 | 70 | @Override 71 | public void addCloseListener(ActionListener listener) { 72 | close.addActionListener(listener); 73 | graph.repaint(); 74 | } 75 | 76 | @Override 77 | public void contentsChanged(ReportSource source) { 78 | // no-op 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/about.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/detail.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/filters.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/graph.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/larger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/larger.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/next.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/open.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/prev.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/probe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/probe.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/resize.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/smaller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/smaller.png -------------------------------------------------------------------------------- /gumshoe-tools/src/main/resources/stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/main/resources/stats.png -------------------------------------------------------------------------------- /gumshoe-tools/src/test/java/com/dell/gumshoe/tools/TestFileReader.java: -------------------------------------------------------------------------------- 1 | package com.dell.gumshoe.tools; 2 | 3 | import com.dell.gumshoe.inspector.FileDataParser; 4 | import com.dell.gumshoe.stack.Stack; 5 | import com.dell.gumshoe.stats.StatisticAdder; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Date; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import junit.framework.TestCase; 14 | 15 | public class TestFileReader extends TestCase { 16 | FileDataParser target; 17 | 18 | @Override 19 | public void setUp() throws Exception { 20 | final String fileName = getClass().getClassLoader().getResource("sample-data.txt").getFile(); 21 | target = new FileDataParser(fileName); 22 | } 23 | 24 | @Override 25 | public void tearDown() throws IOException { 26 | target.close(); 27 | } 28 | 29 | public void testNavigating() throws Exception { 30 | Map entry = target.getNextReport(); 31 | assertEquals(7, entry.size()); 32 | Date time1 = target.getReportTime(); 33 | 34 | entry = target.getNextReport(); 35 | Date time2 = target.getReportTime(); 36 | assertEquals(2, entry.size()); 37 | assertTrue(time1.before(time2)); 38 | 39 | entry = target.getNextReport(); 40 | Date time3 = target.getReportTime(); 41 | assertEquals(2, entry.size()); 42 | assertTrue(time2.before(time3)); 43 | 44 | entry = target.getPreviousReport(); 45 | Date time4 = target.getReportTime(); 46 | assertEquals(2, entry.size()); 47 | assertEquals(time2, time4); 48 | 49 | entry = target.getPreviousReport(); 50 | assertNotNull(entry); 51 | assertNotNull(target.getReportTime()); 52 | entry = target.getPreviousReport(); 53 | assertNull(entry); 54 | assertNull(target.getReportTime()); 55 | } 56 | 57 | public void testDump() throws Exception { 58 | target.parseFile(); 59 | List times = new ArrayList<>(target.getReportTimes()); 60 | assertEquals(3, times.size()); 61 | 62 | Date middle = times.get(1); 63 | Map entry = target.getReport(middle); 64 | Date time = target.getReportTime(); 65 | assertEquals(2, entry.size()); 66 | assertEquals(time, middle); 67 | 68 | entry = target.getReport(new Date()); 69 | assertNull(entry); 70 | assertNull(target.getReportTime()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /gumshoe-tools/src/test/resources/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worstcase/gumshoe/0f480e1ca1d6f9eff06aaae2d7cd8c1c174469c6/gumshoe-tools/src/test/resources/.keep -------------------------------------------------------------------------------- /gumshoe-tools/src/test/resources/sample-data.txt: -------------------------------------------------------------------------------- 1 | some other irrelevant stuff 2 | 3 | 0 read ops 0 bytes in 0 ms, 38 write ops 673 bytes in 3 ms: [127.0.0.1:12345] 4 | at javachat.network.socket.SocketController.sendData(SocketController.java:111) 5 | at javachat.network.socket.SocketController.sendMsg(SocketController.java:102) 6 | at javachat.network.Client.setName(Client.java:105) 7 | at javachat.ui.ChatWindow.jButtonUpdateNameActionPerformed(ChatWindow.java:244) 8 | at javachat.ui.ChatWindow.access$600(ChatWindow.java:15) 9 | at javachat.ui.ChatWindow$7.actionPerformed(ChatWindow.java:118) 10 | 2 read ops 4 bytes in 1 ms, 0 write ops 0 bytes in 0 ms: [127.0.0.1:12345] 11 | at javachat.network.socket.SocketController.(SocketController.java:33) 12 | at javachat.network.Client.(Client.java:47) 13 | at javachat.network.Client.createClient(Client.java:29) 14 | at javachat.JavaChat.startClient(JavaChat.java:53) 15 | at javachat.ui.ChatWindow.jToggleButtonOnlineActionPerformed(ChatWindow.java:203) 16 | at javachat.ui.ChatWindow.access$200(ChatWindow.java:15) 17 | at javachat.ui.ChatWindow$3.actionPerformed(ChatWindow.java:81) 18 | 291 read ops 1302 bytes in 19169 ms, 0 write ops 0 bytes in 0 ms: [127.0.0.1:12345] 19 | at javachat.network.socket.SocketController.run(SocketController.java:76) 20 | 0 read ops 0 bytes in 0 ms, 1 write ops 4 bytes in 1 ms: [127.0.0.1:12345] 21 | at javachat.network.socket.SocketController.(SocketController.java:32) 22 | at javachat.network.Client.(Client.java:47) 23 | at javachat.network.Client.createClient(Client.java:29) 24 | at javachat.JavaChat.startClient(JavaChat.java:53) 25 | at javachat.ui.ChatWindow.jToggleButtonOnlineActionPerformed(ChatWindow.java:203) 26 | at javachat.ui.ChatWindow.access$200(ChatWindow.java:15) 27 | at javachat.ui.ChatWindow$3.actionPerformed(ChatWindow.java:81) 28 | 0 read ops 0 bytes in 0 ms, 6 write ops 262 bytes in 1 ms: [127.0.0.1:12345] 29 | at javachat.network.socket.SocketController.sendData(SocketController.java:111) 30 | at javachat.network.socket.SocketController.sendMsg(SocketController.java:102) 31 | at javachat.network.Client.sendHello(Client.java:98) 32 | at javachat.network.Client.createClient(Client.java:32) 33 | at javachat.JavaChat.startClient(JavaChat.java:53) 34 | at javachat.ui.ChatWindow.jToggleButtonOnlineActionPerformed(ChatWindow.java:203) 35 | at javachat.ui.ChatWindow.access$200(ChatWindow.java:15) 36 | at javachat.ui.ChatWindow$3.actionPerformed(ChatWindow.java:81) 37 | 0 read ops 0 bytes in 0 ms, 8 write ops 192 bytes in 1 ms: [127.0.0.1:12345] 38 | at javachat.network.socket.SocketController.sendData(SocketController.java:111) 39 | at javachat.network.socket.SocketController.sendMsg(SocketController.java:98) 40 | at javachat.network.Client.sendMsg(Client.java:93) 41 | at javachat.ui.ChatWindow.jButtonSendActionPerformed(ChatWindow.java:222) 42 | at javachat.ui.ChatWindow.access$400(ChatWindow.java:15) 43 | at javachat.ui.ChatWindow$5.actionPerformed(ChatWindow.java:99) 44 | 0 read ops 0 bytes in 0 ms, 2 write ops 20 bytes in 1 ms: [127.0.0.1:12345] 45 | at javachat.network.socket.SocketController.sendData(SocketController.java:111) 46 | at javachat.network.socket.SocketController.sendMsg(SocketController.java:102) 47 | at javachat.network.Client.receiveMsg(Client.java:59) 48 | at javachat.network.socket.SocketController.run(SocketController.java:77) 49 | 50 | some other irrelevant stuff 51 | 52 | 294 read ops 1284 bytes in 59511 ms, 0 write ops 0 bytes in 0 ms: [127.0.0.1:12345] 53 | at javachat.network.socket.SocketController.run(SocketController.java:76) 54 | 0 read ops 0 bytes in 0 ms, 2 write ops 12 bytes in 1 ms: [127.0.0.1:12345] 55 | at javachat.network.socket.SocketController.sendData(SocketController.java:111) 56 | at javachat.network.socket.SocketController.sendMsg(SocketController.java:102) 57 | at javachat.network.Client.receiveMsg(Client.java:59) 58 | at javachat.network.socket.SocketController.run(SocketController.java:77) 59 | 60 | some other irrelevant stuff 61 | 62 | 6 read ops 12 bytes in 61045 ms, 0 write ops 0 bytes in 0 ms: [127.0.0.1:12345] 63 | at javachat.network.socket.SocketController.run(SocketController.java:76) 64 | 0 read ops 0 bytes in 0 ms, 2 write ops 12 bytes in 0 ms: [127.0.0.1:12345] 65 | at javachat.network.socket.SocketController.sendData(SocketController.java:111) 66 | at javachat.network.socket.SocketController.sendMsg(SocketController.java:102) 67 | at javachat.network.Client.receiveMsg(Client.java:59) 68 | at javachat.network.socket.SocketController.run(SocketController.java:77) 69 | 70 | some other irrelevant stuff -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.dell 5 | investigator 6 | 0.1.0-SNAPSHOT 7 | pom 8 | Gumshoe Load Investigator 9 | Gumshoe Load Investigator 10 | 11 | 12 | gumshoe-probes 13 | gumshoe-tools 14 | 15 | 16 | 17 | 3.0.0 18 | 19 | 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------