DebugUtil.java
// 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.doris.common.util;
import org.apache.doris.common.Pair;
import org.apache.doris.proto.Types;
import org.apache.doris.thrift.TPlanNodeRuntimeStatsItem;
import org.apache.doris.thrift.TUniqueId;
import com.google.common.base.Strings;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.util.List;
import java.util.UUID;
public class DebugUtil {
public static final DecimalFormat DECIMAL_FORMAT_SCALE_3 = new DecimalFormat("0.000");
public static int THOUSAND = 1000;
public static int MILLION = 1000 * THOUSAND;
public static int BILLION = 1000 * MILLION;
public static int SECOND = 1000; // ms
public static int MINUTE = 60 * SECOND;
public static int HOUR = 60 * MINUTE;
public static long KILOBYTE = 1024;
public static long MEGABYTE = 1024 * KILOBYTE;
public static long GIGABYTE = 1024 * MEGABYTE;
public static long TERABYTE = 1024 * GIGABYTE;
public static Pair<Double, String> getUint(long value) {
Double doubleValue = Double.valueOf(value);
String unit = "";
if (value >= BILLION) {
unit = "B";
doubleValue /= BILLION;
} else if (value >= MILLION) {
unit = "M";
doubleValue /= MILLION;
} else if (value >= THOUSAND) {
unit = "K";
doubleValue /= THOUSAND;
}
Pair<Double, String> returnValue = Pair.of(doubleValue, unit);
return returnValue;
}
// Print the value (timestamp in ms) to builder
// ATTN: for hour and minute granularity, we ignore ms precision.
public static void printTimeMs(long value, StringBuilder builder) {
long newValue = value;
if (newValue == 0) {
builder.append("0");
} else {
boolean hour = false;
boolean minute = false;
if (newValue >= HOUR) {
builder.append(newValue / HOUR).append("hour");
newValue %= HOUR;
hour = true;
}
if (newValue >= MINUTE) {
builder.append(newValue / MINUTE).append("min");
newValue %= MINUTE;
minute = true;
}
if (!hour && newValue >= SECOND) {
builder.append(newValue / SECOND).append("sec");
newValue %= SECOND;
}
if (!hour && !minute) {
builder.append(newValue).append("ms");
}
}
}
public static String getPrettyStringMs(long timestampMs) {
StringBuilder builder = new StringBuilder();
printTimeMs(timestampMs, builder);
return builder.toString();
}
public static Pair<Double, String> getByteUint(long value) {
Double doubleValue = Double.valueOf(value);
String unit = "";
if (value == 0) {
// nothing
unit = "";
} else if (value > TERABYTE) {
unit = "TB";
doubleValue /= TERABYTE;
} else if (value > GIGABYTE) {
unit = "GB";
doubleValue /= GIGABYTE;
} else if (value > MEGABYTE) {
unit = "MB";
doubleValue /= MEGABYTE;
} else if (value > KILOBYTE) {
unit = "KB";
doubleValue /= KILOBYTE;
} else {
unit = "B";
}
Pair<Double, String> returnValue = Pair.of(doubleValue, unit);
return returnValue;
}
public static String printByteWithUnit(long value) {
Pair<Double, String> quotaUnitPair = getByteUint(value);
String readableQuota = DebugUtil.DECIMAL_FORMAT_SCALE_3.format(quotaUnitPair.first) + " "
+ quotaUnitPair.second;
return readableQuota;
}
public static String printId(final TUniqueId id) {
if (id == null) {
return "";
}
StringBuilder builder = new StringBuilder();
builder.append(Long.toHexString(id.hi)).append("-").append(Long.toHexString(id.lo));
return builder.toString();
}
// id is a String generated by DebugUtil.printId(TUniqueId)
public static TUniqueId parseTUniqueIdFromString(String id) {
if (Strings.isNullOrEmpty(id)) {
throw new NumberFormatException("invalid query id");
}
String[] parts = id.split("-");
if (parts.length != 2) {
throw new NumberFormatException("invalid query id");
}
TUniqueId uniqueId = new TUniqueId();
try {
uniqueId.setHi(Long.parseUnsignedLong(parts[0], 16));
uniqueId.setLo(Long.parseUnsignedLong(parts[1], 16));
} catch (NumberFormatException e) {
throw new NumberFormatException("invalid query id:" + e.getMessage());
}
return uniqueId;
}
public static String printId(final UUID id) {
TUniqueId tUniqueId = new TUniqueId(id.getMostSignificantBits(), id.getLeastSignificantBits());
StringBuilder builder = new StringBuilder();
builder.append(Long.toHexString(tUniqueId.hi)).append("-").append(Long.toHexString(tUniqueId.lo));
return builder.toString();
}
public static String printId(final Types.PUniqueId id) {
StringBuilder builder = new StringBuilder();
builder.append(Long.toHexString(id.getHi())).append("-").append(Long.toHexString(id.getLo()));
return builder.toString();
}
public static String getStackTrace(Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
public static String prettyPrintChangedSessionVar(List<List<String>> nestedList) {
if (nestedList == null || nestedList.isEmpty()) {
return "";
}
StringBuilder output = new StringBuilder();
// Assuming each inner list has exactly 3 columns
int[] columnWidths = new int[3];
// Calculate the maximum width of each column
// First consider the header widths: "VarName", "CurrentValue", "DefaultValue"
String[] headers = {"VarName", "CurrentValue", "DefaultValue"};
for (int i = 0; i < headers.length; i++) {
columnWidths[i] = headers[i].length(); // Initialize with header length
}
// Update column widths based on data
for (List<String> row : nestedList) {
for (int i = 0; i < row.size(); i++) {
columnWidths[i] = Math.max(columnWidths[i], row.get(i).length());
}
}
// Build the table header
for (int i = 0; i < headers.length; i++) {
output.append(String.format("%-" + columnWidths[i] + "s", headers[i]));
if (i < headers.length - 1) {
output.append(" | "); // Separator between columns
}
}
output.append("\n"); // Newline after the header
// Add a separator line for better readability (optional)
for (int i = 0; i < headers.length; i++) {
output.append(String.format("%-" + columnWidths[i] + "s", Strings.repeat("-", columnWidths[i])));
if (i < headers.length - 1) {
output.append("-|-"); // Separator between columns
}
}
output.append("\n"); // Newline after the separator
// Build the table body with proper alignment based on column widths
for (List<String> row : nestedList) {
for (int i = 0; i < row.size(); i++) {
String element = row.get(i);
// Pad with spaces if the element is shorter than the column width
output.append(String.format("%-" + columnWidths[i] + "s", element));
if (i < row.size() - 1) {
output.append(" | "); // Separator between columns
}
}
output.append("\n"); // Newline after each row
}
return output.toString();
}
public static String prettyPrintPlanNodeRuntimeStatsItems(
List<TPlanNodeRuntimeStatsItem> planNodeRuntimeStatsItems) {
StringBuilder result = new StringBuilder();
if (planNodeRuntimeStatsItems == null || planNodeRuntimeStatsItems.isEmpty()) {
result.append("The list is empty or null.\n");
return result.toString();
}
result.append(String.format("%-10s %-10s %-15s %-15s %-15s %-15s %-15s %-15s %-15s %-15s %-10s %-10s\n",
"NodeID", "InstanceNum", "InputRows", "OutputRows", "CommonFilterRows", "CommonFilterInputRows",
"RuntimeFilterRows", "RuntimeFilterInputRows", "JoinBuilderRows", "JoinProbeRows",
"JoinBuilderSkewRatio", "JoinProbeSkewRatio"));
for (TPlanNodeRuntimeStatsItem item : planNodeRuntimeStatsItems) {
result.append(String.format("%-10d %-10d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-10d %-10d\n",
item.getNodeId(),
item.getInstanceNum(),
item.getInputRows(),
item.getOutputRows(),
item.getCommonFilterRows(),
item.getCommonFilterInputRows(),
item.getRuntimeFilterRows(),
item.getRuntimeFilterInputRows(),
item.getJoinBuilderRows(),
item.getJoinProbeRows(),
item.getJoinBuilderSkewRatio(),
item.getJoinProberSkewRatio()
));
}
return result.toString();
}
}