mirror of
https://github.com/FabricMC/fabric.git
synced 2025-03-20 20:21:28 -04:00
Print the full stack trace from the dedicated server watchdog (#4052)
* Port FullSTackWatchdog * spotless * checkstyle * fixed package * added test mod * checkstyle
This commit is contained in:
parent
64afec5a95
commit
4854df7166
6 changed files with 211 additions and 0 deletions
|
@ -1 +1,5 @@
|
||||||
version = getSubprojectVersion(project)
|
version = getSubprojectVersion(project)
|
||||||
|
|
||||||
|
testDependencies(project, [
|
||||||
|
':fabric-command-api-v2'
|
||||||
|
])
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||||
|
*
|
||||||
|
* 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 net.fabricmc.fabric.impl.crash.report.info;
|
||||||
|
|
||||||
|
import java.lang.management.LockInfo;
|
||||||
|
import java.lang.management.MonitorInfo;
|
||||||
|
import java.lang.management.ThreadInfo;
|
||||||
|
|
||||||
|
public class ThreadPrinting {
|
||||||
|
/**
|
||||||
|
* A modified copy of {@link ThreadInfo#toString} without the MAX_FRAMES check.
|
||||||
|
*/
|
||||||
|
public static String fullThreadInfoToString(ThreadInfo threadInfo) {
|
||||||
|
StringBuilder sb = new StringBuilder("\"" + threadInfo.getThreadName() + "\""
|
||||||
|
+ (threadInfo.isDaemon() ? " daemon" : "")
|
||||||
|
+ " prio=" + threadInfo.getPriority()
|
||||||
|
+ " Id=" + threadInfo.getThreadId() + " "
|
||||||
|
+ threadInfo.getThreadState());
|
||||||
|
|
||||||
|
if (threadInfo.getLockName() != null) {
|
||||||
|
sb.append(" on ").append(threadInfo.getLockName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadInfo.getLockOwnerName() != null) {
|
||||||
|
sb.append(" owned by \"").append(threadInfo.getLockOwnerName())
|
||||||
|
.append("\" Id=").append(threadInfo.getLockOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadInfo.isSuspended()) {
|
||||||
|
sb.append(" (suspended)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadInfo.isInNative()) {
|
||||||
|
sb.append(" (in native)");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append('\n');
|
||||||
|
|
||||||
|
StackTraceElement[] stackTraceElements = threadInfo.getStackTrace();
|
||||||
|
|
||||||
|
for (int i = 0; i < stackTraceElements.length; i++) {
|
||||||
|
StackTraceElement ste = stackTraceElements[i];
|
||||||
|
sb.append("\tat ").append(ste.toString());
|
||||||
|
sb.append('\n');
|
||||||
|
|
||||||
|
if (i == 0 && threadInfo.getLockInfo() != null) {
|
||||||
|
Thread.State ts = threadInfo.getThreadState();
|
||||||
|
switch (ts) {
|
||||||
|
case BLOCKED -> {
|
||||||
|
sb.append("\t- blocked on ").append(threadInfo.getLockInfo());
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
case WAITING, TIMED_WAITING -> {
|
||||||
|
sb.append("\t- waiting on ").append(threadInfo.getLockInfo());
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MonitorInfo mi : threadInfo.getLockedMonitors()) {
|
||||||
|
if (mi.getLockedStackDepth() == i) {
|
||||||
|
sb.append("\t- locked ").append(mi);
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LockInfo[] locks = threadInfo.getLockedSynchronizers();
|
||||||
|
|
||||||
|
if (locks.length > 0) {
|
||||||
|
sb.append("\n\tNumber of locked synchronizers = ").append(locks.length);
|
||||||
|
sb.append('\n');
|
||||||
|
|
||||||
|
for (LockInfo li : locks) {
|
||||||
|
sb.append("\t- ").append(li);
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append('\n');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||||
|
*
|
||||||
|
* 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 net.fabricmc.fabric.mixin.crash.report.info;
|
||||||
|
|
||||||
|
import java.lang.management.ThreadInfo;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
||||||
|
import net.minecraft.server.dedicated.DedicatedServerWatchdog;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.impl.crash.report.info.ThreadPrinting;
|
||||||
|
|
||||||
|
@Mixin(DedicatedServerWatchdog.class)
|
||||||
|
public class DedicatedServerWatchdogMixin {
|
||||||
|
@ModifyArg(method = "createCrashReport(Ljava/lang/String;J)Lnet/minecraft/util/crash/CrashReport;",
|
||||||
|
at = @At(value = "INVOKE",
|
||||||
|
target = "Ljava/lang/StringBuilder;append(Ljava/lang/Object;)Ljava/lang/StringBuilder;",
|
||||||
|
ordinal = 0,
|
||||||
|
remap = false)
|
||||||
|
)
|
||||||
|
private static Object printEntireThreadDump(Object object) {
|
||||||
|
if (object instanceof ThreadInfo threadInfo) {
|
||||||
|
return ThreadPrinting.fullThreadInfoToString(threadInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
"package": "net.fabricmc.fabric.mixin.crash.report.info",
|
"package": "net.fabricmc.fabric.mixin.crash.report.info",
|
||||||
"compatibilityLevel": "JAVA_21",
|
"compatibilityLevel": "JAVA_21",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
|
"DedicatedServerWatchdogMixin",
|
||||||
"SystemDetailsMixin"
|
"SystemDetailsMixin"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||||
|
*
|
||||||
|
* 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 net.fabricmc.fabric.test.crash.report.info;
|
||||||
|
|
||||||
|
import static net.minecraft.server.command.CommandManager.literal;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.server.dedicated.DedicatedServerWatchdog;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.crash.CrashReport;
|
||||||
|
import net.minecraft.util.crash.ReportType;
|
||||||
|
|
||||||
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||||
|
|
||||||
|
public class ThreadDumpTests implements ModInitializer {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadDumpTests.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) ->
|
||||||
|
dispatcher.register(literal("print_thread_dump_test_command").executes(this::executeDumpCommand)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeDumpCommand(CommandContext<ServerCommandSource> context) {
|
||||||
|
final ServerCommandSource source = context.getSource();
|
||||||
|
CrashReport crashReport = DedicatedServerWatchdog.createCrashReport("Watching Server", context.getSource().getServer().getThread().threadId());
|
||||||
|
LOGGER.info(crashReport.asString(ReportType.MINECRAFT_CRASH_REPORT));
|
||||||
|
source.sendFeedback(() -> Text.literal("Thread Dump printed to console."), false);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "fabric-crash-report-info-v1-testmod",
|
||||||
|
"name": "Fabric Crash Report Info (v1) Test Mod",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"environment": "*",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"entrypoints": {
|
||||||
|
"main": [
|
||||||
|
"net.fabricmc.fabric.test.crash.report.info.ThreadDumpTests"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue