/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed 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 com.intellij.reporting;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
public class FreezeLoggerImpl extends FreezeLogger {
private static final Logger LOG = Logger.getInstance(FreezeLoggerImpl.class);
private static final Alarm ALARM = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, ApplicationManager.getApplication());
private static final int MAX_ALLOWED_TIME = 500;
@Override
public void runUnderPerformanceMonitor(@Nullable Project project, @NotNull Runnable action) {
if (!shouldReport() || isUnderDebug() || ApplicationManager.getApplication().isUnitTestMode()) {
action.run();
return;
}
final ModalityState initial = ModalityState.current();
ALARM.cancelAllRequests();
ALARM.addRequest(() -> dumpThreads(project, initial), MAX_ALLOWED_TIME);
try {
action.run();
}
finally {
ALARM.cancelAllRequests();
}
}
private static boolean shouldReport() {
return Registry.is("typing.freeze.report.dumps");
}
private static void dumpThreads(@Nullable Project project, @NotNull ModalityState initialState) {
final ThreadInfo[] infos = ThreadDumper.getThreadInfos();
final String edtTrace = ThreadDumper.dumpEdtStackTrace(infos);
if (edtTrace.contains("java.lang.ClassLoader.loadClass")) {
return;
}
final boolean isInDumbMode = project != null && !project.isDisposed() && DumbService.isDumb(project);
ApplicationManager.getApplication().invokeLater(() -> {
if (!initialState.equals(ModalityState.current())) return;
sendDumpsInBackground(infos, isInDumbMode);
}, ModalityState.any());
}
private static void sendDumpsInBackground(ThreadInfo[] infos, boolean isInDumbMode) {
//FIXME [VISTALL] we need this?
/*ApplicationManager.getApplication().executeOnPooledThread(() -> {
ThreadDumpInfo info = new ThreadDumpInfo(infos, isInDumbMode);
String report = ReporterKt.createReportLine("typing-freeze-dumps", info);
if (!StatsSender.INSTANCE.send(report, true)) {
LOG.debug("Error while reporting thread dump");
}
}); */
}
private static boolean isUnderDebug() {
return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp");
}
}
class ThreadDumpInfo {
public final ThreadInfo[] threadInfos;
public final String version;
public final String product;
public final String buildNumber;
public final boolean isEAP;
public final boolean isInDumbMode;
public ThreadDumpInfo(ThreadInfo[] threadInfos, boolean isInDumbMode) {
this.threadInfos = threadInfos;
this.product = ApplicationInfo.getInstance().getVersionName();
this.version = ApplicationInfo.getInstance().getFullVersion();
this.buildNumber = ApplicationInfo.getInstance().getBuild().toString();
this.isEAP = ApplicationManager.getApplication().isEAP();
this.isInDumbMode = isInDumbMode;
}
}