blob: a94102bd0fa3bbc1ddd4c9790f6a038d3f3230b0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.run;
import static java.util.Arrays.asList;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.apache.jackrabbit.oak.commons.Profiler;
import org.apache.jackrabbit.oak.run.commons.Command;
import org.apache.jackrabbit.oak.threadDump.ThreadDumpThreadNames;
import org.apache.jackrabbit.oak.threadDump.ThreadDumpCleaner;
import org.apache.jackrabbit.oak.threadDump.ThreadDumpConverter;
public class ThreadDumpCommand implements Command {
public final static String THREADDUMP = "threaddump";
@SuppressWarnings("unchecked")
@Override
public void execute(String... args) throws Exception {
OptionParser parser = new OptionParser();
OptionSpec<Void> convertSpec = parser.accepts("convert",
"convert the thread dumps to the standard format");
OptionSpec<Void> filterSpec = parser.accepts("filter",
"filter the thread dumps, only keep working (running), interesting threads " +
"(for example, threads that read from sockets are ignored, " +
"as they are typically waiting for input; " +
"system threads such as GC are also ignored)");
OptionSpec<Void> threadNamesSpec = parser.accepts("threadNames",
"create a summary of thread names");
OptionSpec<Void> profileSpec = parser.accepts("profile",
"profile the thread dumps");
OptionSpec<Void> profileClassesSpec = parser.accepts("profileClasses",
"profile classes");
OptionSpec<Void> profileMethodsSpec = parser.accepts("profileMethods",
"profile methods");
OptionSpec<Void> profilePackagesSpec = parser.accepts("profilePackages",
"profile packages");
OptionSpec<?> helpSpec = parser.acceptsAll(
asList("h", "?", "help"), "show help").forHelp();
OptionSet options = parser.parse(args);
parser.nonOptions(
"file or directory containing thread dumps " +
"(ensure it does not contain other files, such as binaries)").ofType(File.class);
if (options.has(helpSpec)
|| options.nonOptionArguments().isEmpty()) {
System.out.println("Mode: " + THREADDUMP);
System.out.println();
parser.printHelpOn(System.out);
return;
}
boolean convert = options.has(convertSpec);
boolean filter = options.has(filterSpec);
boolean threadNames = options.has(threadNamesSpec);
boolean profile = options.has(profileSpec);
boolean profileClasses = options.has(profileClassesSpec);
boolean profileMethods = options.has(profileMethodsSpec);
boolean profilePackages = options.has(profilePackagesSpec);
for (String fileName : ((List<String>) options.nonOptionArguments())) {
File file = new File(fileName);
if (file.isDirectory() || file.getName().endsWith(".gz")) {
file = combineAndExpandFiles(file);
System.out.println("Combined into " + file.getAbsolutePath());
}
if (convert) {
file = ThreadDumpConverter.process(file);
System.out.println("Converted to " + file.getAbsolutePath());
}
if (threadNames) {
File f = ThreadDumpThreadNames.process(file);
System.out.println("Thread names written to " + f.getAbsolutePath());
}
if (filter) {
file = ThreadDumpCleaner.process(file);
System.out.println("Filtered into " + file.getAbsolutePath());
}
if (threadNames) {
File f = ThreadDumpThreadNames.process(file);
System.out.println("Thread names written to " + f.getAbsolutePath());
}
if (profile) {
ArrayList<String> list = new ArrayList<String>();
if (profileClasses) {
list.add("-classes");
}
if (profileMethods) {
list.add("-methods");
}
if (profilePackages) {
list.add("-packages");
}
list.add(file.getAbsolutePath());
Profiler.main(list.toArray(new String[0]));
}
}
}
private static File combineAndExpandFiles(File file) throws IOException {
if (!file.exists() || !file.isDirectory()) {
if (!file.getName().endsWith(".gz") && !file.getName().endsWith(".zip")) {
return file;
}
}
File target = new File(file.getParentFile(), file.getName() + ".txt");
PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(
target)));
try {
int count = processFileOrDirectory(file, writer);
System.out.println(" (total " + count + " full thread dumps)");
} finally {
writer.close();
}
return target;
}
private static int processFileOrDirectory(File file, PrintWriter writer)
throws IOException {
if (file.isFile()) {
return processFile(file, writer);
}
int count = 0;
File[] list = file.listFiles();
if (list != null) {
for(File f : list) {
count += processFileOrDirectory(f, writer);
}
}
return count;
}
private static int processFile(File file, PrintWriter writer) throws IOException {
Reader reader = null;
int count = 0;
try {
if (file.getName().endsWith(".DS_Store")) {
return 0;
}
int fullThreadDumps = 0;
String fileModifiedTime = new Timestamp(file.lastModified()).toString();
writer.write("file " + file.getAbsolutePath() + "\n");
writer.write("lastModified " + fileModifiedTime + "\n");
if (file.getName().endsWith(".gz")) {
// System.out.println("Extracting " + file.getAbsolutePath());
InputStream fileStream = new FileInputStream(file);
try {
InputStream gzipStream = new GZIPInputStream(fileStream);
reader = new InputStreamReader(gzipStream);
} catch (EOFException e) {
fileStream.close();
return 0;
}
} else if (file.getName().endsWith(".zip")) {
System.out.println("Warning: file skipped. Please extract first: " + file.getName());
return 0;
} else {
System.out.println("Reading " + file.getAbsolutePath());
reader = new FileReader(file);
}
LineNumberReader in = new LineNumberReader(new BufferedReader(
reader));
while (true) {
String s;
try {
s = in.readLine();
} catch (EOFException e) {
// EOFException: Unexpected end of ZLIB input stream
break;
} catch (ZipException e) {
// java.util.zip.ZipException: invalid block type
break;
}
if (s == null) {
break;
}
if (s.startsWith("Full thread dump") || s.startsWith("Full Java thread dump")) {
fullThreadDumps++;
}
writer.println(s);
}
if (fullThreadDumps > 0) {
count++;
// System.out.println(" (contains " + fullThreadDumps + " full thread dumps; " + fileModifiedTime + ")");
}
} finally {
if(reader != null) {
reader.close();
}
}
return count;
}
}