diff --git a/bvm/ballerina-profiler/build.gradle b/bvm/ballerina-profiler/build.gradle
index 03bb0fd9497e..dd0b44fe5d35 100644
--- a/bvm/ballerina-profiler/build.gradle
+++ b/bvm/ballerina-profiler/build.gradle
@@ -24,12 +24,14 @@ dependencies {
implementation "com.google.code.gson:gson:${project.gsonVersion}"
implementation "commons-io:commons-io:${project.commonsIoVersion}"
implementation project(':identifier-util')
+ implementation project(':ballerina-runtime')
}
version = 1.0
jar {
dependsOn(':identifier-util:jar')
+ dependsOn(':ballerina-runtime:jar')
from(sourceSets.main.output)
from(sourceSets.main.java) {
include "**/*.java"
@@ -43,11 +45,6 @@ jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
- attributes 'Implementation-Title': 'Foobar',
- 'Implementation-Version': '',
- 'Built-By': System.getProperty('user.name'),
- 'Built-Date': new Date(),
- 'Built-JDK': System.getProperty('java.version'),
- 'Main-Class': 'io.ballerina.runtime.profiler.Main'
+ attributes 'Main-Class': 'io.ballerina.runtime.profiler.Main'
}
}
diff --git a/bvm/ballerina-profiler/spotbugs-exclude.xml b/bvm/ballerina-profiler/spotbugs-exclude.xml
index 51596bdbb759..ec6fc1f4f90c 100644
--- a/bvm/ballerina-profiler/spotbugs-exclude.xml
+++ b/bvm/ballerina-profiler/spotbugs-exclude.xml
@@ -20,4 +20,8 @@
+
+
+
+
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/Profiler.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/Profiler.java
index 51b0d0a00abe..bbb1d0ab60ea 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/Profiler.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/Profiler.java
@@ -31,17 +31,13 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
@@ -49,7 +45,9 @@
import java.util.zip.ZipInputStream;
import static io.ballerina.runtime.profiler.util.Constants.CPU_PRE_JSON;
+import static io.ballerina.runtime.profiler.util.Constants.CURRENT_DIR_KEY;
import static io.ballerina.runtime.profiler.util.Constants.OUT_STREAM;
+import static io.ballerina.runtime.profiler.util.Constants.PERFORMANCE_JSON;
/**
* This class is used to as the driver class of the Ballerina profiler.
@@ -59,7 +57,6 @@
public class Profiler {
private final long profilerStartTime;
- private String skipFunctionString = null;
private String balJarArgs = null;
private String balJarName = null;
private String targetDir = null;
@@ -72,10 +69,12 @@ public class Profiler {
private int balFunctionCount = 0;
private int moduleCount = 0;
private final ProfilerMethodWrapper profilerMethodWrapper;
+ private final String currentDir;
public Profiler(long profilerStartTime) {
this.profilerStartTime = profilerStartTime;
this.profilerMethodWrapper = new ProfilerMethodWrapper();
+ this.currentDir = System.getenv(CURRENT_DIR_KEY);
}
private void addShutdownHookAndCleanup() {
@@ -88,13 +87,12 @@ private void addShutdownHookAndCleanup() {
OUT_STREAM.printf("%s[6/6] Generating output...%s%n", Constants.ANSI_CYAN, Constants.ANSI_RESET);
JsonParser jsonParser = new JsonParser();
HttpServer httpServer = new HttpServer();
- jsonParser.initializeCPUParser(skipFunctionString);
- deleteFileIfExists("usedPathsList.txt");
- deleteFileIfExists(CPU_PRE_JSON);
+ String cpuFilePath = Paths.get(currentDir, CPU_PRE_JSON).toString();
+ jsonParser.initializeCPUParser(cpuFilePath);
+ deleteFileIfExists(cpuFilePath);
OUT_STREAM.printf(" ○ Execution time: %d seconds %n", profilerTotalTime / 1000);
- deleteTempData();
httpServer.initializeHTMLExport(this.sourceRoot);
- deleteFileIfExists("performance_report.json");
+ deleteFileIfExists(PERFORMANCE_JSON);
OUT_STREAM.println("--------------------------------------------------------------------------------");
} catch (IOException e) {
throw new ProfilerException("Error occurred while generating the output", e);
@@ -116,18 +114,6 @@ private void deleteFileIfExists(String filePath) {
}
}
- private void deleteTempData() {
- String filePrefix = "jartmp";
- File[] files = new File(System.getProperty("user.dir")).listFiles();
- if (files != null) {
- for (File file : files) {
- if (file.getName().startsWith(filePrefix)) {
- FileUtils.deleteQuietly(file);
- }
- }
- }
- }
-
private void printHeader() {
OUT_STREAM.printf("%n%s================================================================================%s",
Constants.ANSI_GRAY, Constants.ANSI_RESET);
@@ -154,19 +140,15 @@ private void handleProfilerArguments(String[] args) {
this.balJarArgs = extractBalJarArgs(args[i + 1]);
addToUsedArgs(args, usedArgs, i);
}
- case "--skip" -> {
- this.skipFunctionString = extractSkipFunctionString(args[i + 1]);
- addToUsedArgs(args, usedArgs, i);
- }
case "--target" -> {
this.targetDir = args[i + 1];
addToUsedArgs(args, usedArgs, i);
}
- case "--sourceroot" -> {
+ case "--source-root" -> {
this.sourceRoot = args[i + 1];
addToUsedArgs(args, usedArgs, i);
}
- case "--profilerDebug" -> {
+ case "--profiler-debug" -> {
this.profilerDebugArg = args[i + 1];
addToUsedArgs(args, usedArgs, i);
}
@@ -189,13 +171,6 @@ private String extractBalJarArgs(String value) {
return value.substring(1, value.length() - 1);
}
- private String extractSkipFunctionString(String value) {
- if (value == null || !value.matches("\\[.*\\]")) {
- throw new ProfilerException("Invalid skip functions found: " + value);
- }
- return value.substring(1, value.length() - 1);
- }
-
private void handleUnrecognizedArgument(String argument, List usedArgs) {
if (!usedArgs.contains(argument)) {
throw new ProfilerException("Unrecognized argument found: " + argument);
@@ -242,16 +217,15 @@ private void initializeProfiling() throws ProfilerException {
new File(balJarName).toURI().toURL()}));
ProfilerClassLoader profilerClassLoader = new ProfilerClassLoader(new URLClassLoader(new URL[]{
new File(balJarName).toURI().toURL()}));
- Set usedPaths = new HashSet<>();
for (String className : classNames) {
- if (mainClassPackage == null) {
+ if (mainClassPackage == null || className.contains("$gen$")) {
continue;
}
if (className.startsWith(mainClassPackage.split("/")[0]) || utilPaths.contains(className)) {
try (InputStream inputStream = jarFile.getInputStream(jarFile.getJarEntry(className))) {
- byte[] code = profilerMethodWrapper.modifyMethods(inputStream);
+ String sourceClassName = className.replace(Constants.CLASS_SUFFIX, "");
+ byte[] code = profilerMethodWrapper.modifyMethods(inputStream, sourceClassName);
profilerClassLoader.loadClass(code);
- usedPaths.add(className.replace(Constants.CLASS_SUFFIX, "").replace("/", "."));
profilerMethodWrapper.printCode(className, code, getFileNameWithoutExtension(balJarName));
}
}
@@ -260,9 +234,6 @@ private void initializeProfiling() throws ProfilerException {
}
}
OUT_STREAM.printf(" ○ Instrumented module count: %d%n", moduleCount);
- try (PrintWriter printWriter = new PrintWriter("usedPathsList.txt", StandardCharsets.UTF_8)) {
- printWriter.println(String.join(", ", usedPaths));
- }
OUT_STREAM.printf(" ○ Instrumented function count: %d%n", balFunctionCount);
modifyJar();
} catch (Throwable throwable) {
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/NonStrandCheckAdapter.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/NonStrandCheckAdapter.java
deleted file mode 100644
index 7d67330893a4..000000000000
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/NonStrandCheckAdapter.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
- *
- * WSO2 LLC. 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 io.ballerina.runtime.profiler.codegen;
-
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.commons.AdviceAdapter;
-
-import static io.ballerina.runtime.profiler.util.Constants.GET_INSTANCE_DESCRIPTOR;
-import static io.ballerina.runtime.profiler.util.Constants.PROFILE_ANALYZER;
-
-/**
- * This class is used as the advice adapter for the Ballerina profiler.
- * This class only manages the functions that doesn't contain the strand parameter.
- *
- * @since 2201.8.0
- */
-public class NonStrandCheckAdapter extends AdviceAdapter {
-
- public NonStrandCheckAdapter(int access, MethodVisitor mv, String methodName, String description) {
- super(Opcodes.ASM9, mv, access, methodName, description);
- }
-
- @Override
- protected void onMethodEnter() {
- mv.visitMethodInsn(INVOKESTATIC, PROFILE_ANALYZER, "getInstance", GET_INSTANCE_DESCRIPTOR, false);
- mv.visitMethodInsn(INVOKEVIRTUAL, PROFILE_ANALYZER, "start", "()V", false);
- }
-
- @Override
- protected void onMethodExit(int opcode) {
- mv.visitMethodInsn(INVOKESTATIC, PROFILE_ANALYZER, "getInstance", GET_INSTANCE_DESCRIPTOR, false);
- mv.visitMethodInsn(INVOKEVIRTUAL, PROFILE_ANALYZER, "stop", "()V", false);
- }
-}
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerClassVisitor.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerClassVisitor.java
index b8b85812c6f1..30547efb5608 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerClassVisitor.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerClassVisitor.java
@@ -32,19 +32,20 @@
*/
public class ProfilerClassVisitor extends ClassVisitor {
- public ProfilerClassVisitor(ClassVisitor classVisitor) {
+ private final String className;
+
+ public ProfilerClassVisitor(String className, ClassVisitor classVisitor) {
super(Opcodes.ASM9, classVisitor);
+ this.className = className;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
- if (!name.startsWith("$") && desc.startsWith(STRAND_ARG)) {
+ if (desc.startsWith(STRAND_ARG) && !name.contains("$gen$")) {
Main.incrementBalFunctionCount();
+ return new StrandCheckAdapter(className, access, methodVisitor, name, desc, (access & Opcodes.ACC_STATIC));
}
- if (desc.startsWith(STRAND_ARG)) {
- return new StrandCheckAdapter(access, methodVisitor, name, desc, (access & Opcodes.ACC_STATIC));
- }
- return new NonStrandCheckAdapter(access, methodVisitor, name, desc);
+ return methodVisitor;
}
}
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java
index 10f1cd62a1a5..6b1e802a27f0 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java
@@ -34,13 +34,16 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
+import static io.ballerina.runtime.profiler.util.Constants.CURRENT_DIR_KEY;
import static io.ballerina.runtime.profiler.util.Constants.ERROR_STREAM;
import static io.ballerina.runtime.profiler.util.Constants.OUT_STREAM;
+import static io.ballerina.runtime.profiler.util.Constants.USER_DIR;
/**
* This class is used as the method wrapper for the Ballerina profiler.
@@ -57,12 +60,13 @@ public void invokeMethods(String debugArg) throws IOException, InterruptedExcept
if (debugArg != null) {
commands.add(debugArg);
}
- commands.add(Constants.TEMP_JAR_FILE_NAME);
+ commands.add(Paths.get(System.getProperty(USER_DIR), Constants.TEMP_JAR_FILE_NAME).toString());
if (balJarArgs != null) {
commands.add(balJarArgs);
}
ProcessBuilder processBuilder = new ProcessBuilder(commands);
- processBuilder.redirectErrorStream(true);
+ processBuilder.inheritIO();
+ processBuilder.directory(new File(System.getenv(CURRENT_DIR_KEY)));
Process process = processBuilder.start();
OUT_STREAM.printf(Constants.ANSI_CYAN + "[5/6] Running executable..." + Constants.ANSI_RESET + "%n");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(),
@@ -84,13 +88,13 @@ public String mainClassFinder(URLClassLoader manifestClassLoader) {
}
}
- public byte[] modifyMethods(InputStream inputStream) {
+ public byte[] modifyMethods(InputStream inputStream, String className) {
byte[] code;
try {
ClassReader reader = new ClassReader(inputStream);
ClassWriter classWriter = new ProfilerClassWriter(reader, ClassWriter.COMPUTE_MAXS |
ClassWriter.COMPUTE_FRAMES);
- ClassVisitor change = new ProfilerClassVisitor(classWriter);
+ ClassVisitor change = new ProfilerClassVisitor(className, classWriter);
reader.accept(change, ClassReader.EXPAND_FRAMES);
code = classWriter.toByteArray();
return code;
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/StrandCheckAdapter.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/StrandCheckAdapter.java
index dc77cb595f04..5934bfda0835 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/StrandCheckAdapter.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/StrandCheckAdapter.java
@@ -18,33 +18,48 @@
package io.ballerina.runtime.profiler.codegen;
+import io.ballerina.runtime.internal.scheduling.Strand;
+import io.ballerina.runtime.profiler.runtime.Data;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;
+import static io.ballerina.runtime.profiler.util.Constants.DATA_CLASS;
import static io.ballerina.runtime.profiler.util.Constants.GET_INSTANCE_DESCRIPTOR;
import static io.ballerina.runtime.profiler.util.Constants.PROFILE_ANALYZER;
-import static io.ballerina.runtime.profiler.util.Constants.STRAND_CLASS;
+import static io.ballerina.runtime.profiler.util.Constants.START_PROFILE_DESCRIPTOR;
+import static io.ballerina.runtime.profiler.util.Constants.STOP_PROFILE_DESCRIPTOR;
/**
* This class is used as the advice adapter for the Ballerina profiler.
- * This class only manages the functions that contain the strand parameter.
+ * This will wrap the ballerina methods with
+ * {@link io.ballerina.runtime.profiler.runtime.ProfileAnalyzer#start(Strand, String, String)} and
+ * {@link io.ballerina.runtime.profiler.runtime.ProfileAnalyzer#stop(Strand, Data)} methods.
*
* @since 2201.8.0
*/
public class StrandCheckAdapter extends AdviceAdapter {
+ private final String className;
+ private final String methodName;
+ private final Type dataType;
Label tryStart = new Label();
int load;
+ int stackKeyIndex;
- public StrandCheckAdapter(int access, MethodVisitor mv, String methodName, String description, int load) {
+ public StrandCheckAdapter(String className, int access, MethodVisitor mv, String methodName,
+ String description, int load) {
super(Opcodes.ASM9, mv, access, methodName, description);
+ this.className = className;
+ this.methodName = methodName;
if (load == 0) {
this.load = 1;
} else {
this.load = 0;
}
+ this.dataType = Type.getObjectType(DATA_CLASS);
}
// It adds a label to the try block of the wrapped method.
@@ -57,10 +72,13 @@ public void visitCode() {
// It retrieves the profiler instance, gets the strand id and starts the profiling.
@Override
protected void onMethodEnter() {
+ this.stackKeyIndex = this.newLocal(dataType);
mv.visitMethodInsn(INVOKESTATIC, PROFILE_ANALYZER, "getInstance", GET_INSTANCE_DESCRIPTOR, false);
mv.visitVarInsn(ALOAD, load);
- mv.visitMethodInsn(INVOKEVIRTUAL, STRAND_CLASS, "getId", "()I", false);
- mv.visitMethodInsn(INVOKEVIRTUAL, PROFILE_ANALYZER, "start", "(I)V", false);
+ mv.visitLdcInsn(className);
+ mv.visitLdcInsn(methodName);
+ mv.visitMethodInsn(INVOKEVIRTUAL, PROFILE_ANALYZER, "start", START_PROFILE_DESCRIPTOR, false);
+ mv.visitVarInsn(ASTORE, this.stackKeyIndex);
}
// If the exit is not due to an exception, it calls the onFinally method.
@@ -88,12 +106,7 @@ public void visitMaxs(int maxStack, int maxLocals) {
private void onFinally() {
mv.visitMethodInsn(INVOKESTATIC, PROFILE_ANALYZER, "getInstance", GET_INSTANCE_DESCRIPTOR, false);
mv.visitVarInsn(ALOAD, load);
- mv.visitMethodInsn(INVOKEVIRTUAL, STRAND_CLASS,
- "getState", "()Lio/ballerina/runtime/internal/scheduling/State;", false);
- mv.visitMethodInsn(INVOKEVIRTUAL, "io/ballerina/runtime/internal/scheduling/State",
- "toString", "()Ljava/lang/String;", false);
- mv.visitVarInsn(ALOAD, load);
- mv.visitMethodInsn(INVOKEVIRTUAL, STRAND_CLASS, "getId", "()I", false);
- mv.visitMethodInsn(INVOKEVIRTUAL, PROFILE_ANALYZER, "stop", "(Ljava/lang/String;I)V", false);
+ mv.visitVarInsn(ALOAD, this.stackKeyIndex);
+ mv.visitMethodInsn(INVOKEVIRTUAL, PROFILE_ANALYZER, "stop", STOP_PROFILE_DESCRIPTOR, false);
}
}
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/Data.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/Data.java
index 561b23f51033..027e8ea15747 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/Data.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/Data.java
@@ -18,6 +18,7 @@
package io.ballerina.runtime.profiler.runtime;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
@@ -26,48 +27,33 @@
* @since 2201.8.0
*/
public class Data {
- private final String name;
- private long startTime;
- private long totalTime;
- private long minTime;
- private long maxTime;
-
- public Data(String name) {
- this.name = name;
+ protected final String stackKey;
+ protected final String stackIndex;
+ protected String stackTrace = null;
+ public long totalTime;
+ private final ConcurrentHashMap startTimes = new ConcurrentHashMap<>();
+
+ public Data(String stackIndex, String stackKey) {
+ this.stackIndex = stackIndex;
+ this.stackKey = stackKey;
this.totalTime = 0L;
- this.startTime = 0L;
- this.minTime = Long.MAX_VALUE;
- this.maxTime = Long.MIN_VALUE;
}
- public void start() {
- this.startTime = TimeUnit.MILLISECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS);
+ public synchronized void start(String strandId) {
+ if (this.startTimes.containsKey(strandId)) {
+ return;
+ }
+ startTimes.put(strandId, TimeUnit.MILLISECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS));
}
- public void stop() {
- long elapsed = TimeUnit.MILLISECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS) - this.startTime;
- if (elapsed < this.minTime) {
- this.minTime = elapsed;
- }
- if (elapsed > this.maxTime) {
- this.maxTime = elapsed;
- }
+ public synchronized void stop(String strandId) {
+ long elapsed = TimeUnit.MILLISECONDS.convert(System.nanoTime(),
+ TimeUnit.NANOSECONDS) - this.startTimes.remove(strandId);
this.totalTime += elapsed;
}
private String getFormattedStats() {
- int time = (int) this.totalTime;
- String[] stackTrace = new String[]{this.name};
-
- StringBuilder sb = new StringBuilder();
- sb.append("{");
- sb.append("\"time\": \"").append(time).append("\", ");
- sb.append("\"stackTrace\": ");
- for (String s : stackTrace) {
- sb.append(s);
- }
- sb.append("},");
- return sb.toString();
+ return "{" + "\"time\": \"" + this.totalTime + "\", " + "\"stackTrace\": " + this.stackTrace + "}";
}
@Override
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/ProfileAnalyzer.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/ProfileAnalyzer.java
index 8a170fdba260..a704a6ccfbb5 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/ProfileAnalyzer.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/ProfileAnalyzer.java
@@ -18,22 +18,18 @@
package io.ballerina.runtime.profiler.runtime;
-import io.ballerina.identifier.Utils;
+import io.ballerina.runtime.internal.scheduling.State;
+import io.ballerina.runtime.internal.scheduling.Strand;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static io.ballerina.runtime.profiler.util.Constants.CPU_PRE_JSON;
+import static io.ballerina.runtime.profiler.util.Constants.STRAND_PROFILER_STACK_PROPERTY;
/**
* This class is used as the main profiler class for the Ballerina profiler.
@@ -42,12 +38,7 @@
*/
public class ProfileAnalyzer {
- private final HashMap profiles = new HashMap<>();
- private final ArrayList profilesStack = new ArrayList<>();
- private final Set blockedMethods = new HashSet<>();
- private static final List skippedList = new ArrayList<>();
- private static final Set skippedClasses = new HashSet<>(skippedList);
- private static final String CPU_PRE_JSON = "cpu_pre.json";
+ private final ConcurrentHashMap profiles = new ConcurrentHashMap<>();
private static class ProfilerHolder {
private static final ProfileAnalyzer PROFILER_INSTANCE = new ProfileAnalyzer();
@@ -59,77 +50,48 @@ public static ProfileAnalyzer getInstance() {
private ProfileAnalyzer() {
addProfilerShutDownHook();
- try {
- String content = Files.readString(Paths.get("usedPathsList.txt"));
- List skippedListRead = new ArrayList<>(Arrays.asList(content.split(", ")));
- skippedList.addAll(skippedListRead);
- skippedClasses.addAll(skippedList);
- } catch (IOException ignored) {
- throw new ProfilerRuntimeException("Error occurred while reading the usedPathsList.txt file");
- }
}
- private String getMethodName() {
- final List stack = StackWalker.getInstance().walk(s -> s.collect(Collectors.toList()));
- return stack.get(2).getMethodName() + "()";
- }
-
- public void start(int id) {
- if (!blockedMethods.contains(getMethodName() + id)) {
- ArrayList stackTrace = getStackTrace();
- String stackKey = StackTraceMap.getStackKey(stackTrace);
- Data p;
- if (this.profiles.containsKey(stackKey)) {
- p = this.profiles.get(stackKey);
- } else {
- Data newData = new Data(stackTrace.toString());
- this.profiles.put(stackKey, newData);
- this.profilesStack.add(newData);
- p = newData;
- }
- p.start();
- }
- blockedMethods.remove(getMethodName() + id);
- }
-
- public void start() {
- ArrayList stackTrace = getStackTrace();
- String stackKey = StackTraceMap.getStackKey(stackTrace);
- Data p = this.profiles.get(stackKey);
- if (p == null) {
- p = new Data(stackTrace.toString());
- this.profiles.put(stackKey, p);
- this.profilesStack.add(p);
+ public Data start(Strand strand, String className, String methodName) {
+ Object stacksObj = strand.getProperty(STRAND_PROFILER_STACK_PROPERTY);
+ String stackIndex = StackTraceMap.getStackIndex(className + "." + methodName) + "$";
+ String stackKey;
+ if (stacksObj != null) {
+ stackKey = stacksObj + stackIndex;
+ } else {
+ stackKey = stackIndex;
}
- p.start();
- }
-
- public void stop(String strandState, int id) {
- String stackKey = StackTraceMap.getStackKey(getStackTrace());
- Data p = this.profiles.get(stackKey);
- if (strandState.equals("RUNNABLE")) {
- if (p != null) {
- p.stop();
- }
+ Data data;
+ if (this.profiles.containsKey(stackKey)) {
+ data = this.profiles.get(stackKey);
} else {
- blockedMethods.add(getMethodName() + id);
+ data = new Data(stackIndex, stackKey);
+ this.profiles.put(stackKey, data);
}
+ data.start(String.valueOf(strand.getId()));
+ strand.setProperty(STRAND_PROFILER_STACK_PROPERTY, stackKey);
+ return data;
}
- public void stop() {
- String stackKey = StackTraceMap.getStackKey(getStackTrace());
- Data p = this.profiles.get(stackKey);
- if (p != null) {
- p.stop();
+ public void stop(Strand strand, Data data) {
+ if (strand.getState().equals(State.RUNNABLE)) {
+ data.stop(String.valueOf(strand.getId()));
}
+ strand.setProperty(STRAND_PROFILER_STACK_PROPERTY, data.stackKey.substring(0,
+ data.stackKey.length() - data.stackIndex.length()));
}
public final String getProfileStackString() {
StringBuilder sb = new StringBuilder("[");
- ArrayList stackList = new ArrayList<>(this.profilesStack);
- for (Data data : stackList) {
- sb.append(data).append("\n");
+ ArrayList dataList = new ArrayList<>(this.profiles.values());
+ for (int i = 0; i < (dataList.size() - 1); i++) {
+ Data data = dataList.get(i);
+ data.stackTrace = StackTraceMap.getCallStackString(data.stackKey);
+ sb.append(data).append(",\n");
}
+ Data data = dataList.get(dataList.size() - 1);
+ data.stackTrace = StackTraceMap.getCallStackString(data.stackKey);
+ sb.append(data).append("\n");
sb.append("]");
return sb.toString();
}
@@ -149,32 +111,4 @@ private void addProfilerShutDownHook() {
profiler.printProfilerOutput(profiler.getProfileStackString());
}));
}
-
- // This method returns a string representation of the current call stack in the form of a list of strings
- private ArrayList getStackTrace() {
- ArrayList result = new ArrayList<>();
-
- final List stack = StackWalker.getInstance().walk(s -> s.collect(Collectors.toList()));
- //Removes the first 2 stack frames (index 0 and 1) and reverses the order of the remaining stack frames
- stack.subList(0, 2).clear();
- Collections.reverse(stack); //Reverse the collection
-
- for (StackWalker.StackFrame frame : stack) {
- if (skippedClasses.contains(frame.getClassName())) {
- String frameString = frame.toString();
-
- if (frameString.contains("&")) {
- frameString = "\"" + frameString + "\"";
- } else {
- frameString = "\"" + frameString.replaceAll("\\(.*\\)", "") + "()" + "\"";
- int lastDotIndex = frameString.lastIndexOf('.');
- frameString = frameString.substring(0, lastDotIndex).replace('.', '/') +
- frameString.substring(lastDotIndex);
- }
- result.add(Utils.decodeIdentifier(frameString));
- }
- }
-
- return result;
- }
}
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/StackTraceMap.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/StackTraceMap.java
index 9d448f53a28a..8f208758e739 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/StackTraceMap.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/runtime/StackTraceMap.java
@@ -18,9 +18,10 @@
package io.ballerina.runtime.profiler.runtime;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+import io.ballerina.identifier.Utils;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A map that keeps profile stack the index values against the stack-trace.
@@ -29,23 +30,38 @@
*/
public class StackTraceMap {
- private static int localVarIndex = 0;
+ private static final AtomicInteger localVarIndex = new AtomicInteger(0);
+ private static final ConcurrentHashMap stackTraceIndexMap = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap indexStackTraceMap = new ConcurrentHashMap<>();
private StackTraceMap() {
}
- private static final Map stackTraceIndexMap = new HashMap<>();
-
- static String getStackKey(ArrayList stacks) {
- StringBuilder keyBuilder = new StringBuilder();
- for (String stack : stacks) {
- if (stackTraceIndexMap.containsKey(stack)) {
- keyBuilder.append(stackTraceIndexMap.get(stack));
- } else {
- stackTraceIndexMap.put(stack, localVarIndex);
- keyBuilder.append(localVarIndex++);
- }
- }
- return keyBuilder.toString();
+ static String getStackIndex(String stackElement) {
+ Integer stackIndex = stackTraceIndexMap.get(stackElement);
+ if (stackIndex != null) {
+ return String.valueOf(stackIndex);
+ }
+ int index = localVarIndex.getAndIncrement();
+ stackTraceIndexMap.put(stackElement, index);
+ String indexStr = String.valueOf(index);
+ indexStackTraceMap.put(indexStr, stackElement);
+ return indexStr;
+ }
+
+ static String getCallStackString(String stackKey) {
+ String[] stackElements = stackKey.split("\\$");
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int i = 0; i < (stackElements.length - 1); i++) {
+ sb.append("\"").append(decodeStackElement(indexStackTraceMap.get(stackElements[i]))).append("\",");
+ }
+ sb.append("\"").append(decodeStackElement(indexStackTraceMap.get(stackElements[stackElements.length - 1])))
+ .append("\"]");
+ return sb.toString();
+ }
+
+ private static String decodeStackElement(String stackElement) {
+ return Utils.decodeIdentifier(stackElement.replaceAll("\\$value\\$", ""));
}
}
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FileUtils.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FileUtils.java
index 3901f34a1ee3..c7ebaa7f01da 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FileUtils.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FileUtils.java
@@ -18,6 +18,8 @@
package io.ballerina.runtime.profiler.ui;
+import io.ballerina.runtime.profiler.runtime.ProfilerRuntimeException;
+
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -25,15 +27,29 @@
public class FileUtils {
+ // Maximum wait time for the file to be created. This will wait 600*100 ms = 60 s.
+ private static final int MAX_WAIT_TIME_FOR_FILE = 600;
+
+ private FileUtils() {
+ }
+
static String readFileAsString(String file) throws IOException {
Path path = Paths.get(file);
+ int count = 0;
while (!Files.exists(path)) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
+ if (count++ > MAX_WAIT_TIME_FOR_FILE) {
+ throw new ProfilerRuntimeException("File not found: " + file);
}
+ waitForFile();
}
return Files.readString(path);
}
+
+ private static void waitForFile() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
}
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FrontEnd.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FrontEnd.java
index c6ee1763c5c9..8d97a7d10f45 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FrontEnd.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/FrontEnd.java
@@ -48,7 +48,6 @@ String getSiteData(String contents) {
public String readFileAsString() throws IOException {
StringBuilder sb = new StringBuilder();
-
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(FILE_LOCATION)) {
if (inputStream == null) {
throw new ProfilerRuntimeException("resource file not found: " + FILE_LOCATION);
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/HttpServer.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/HttpServer.java
index 35b176f872f7..1ed16745e255 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/HttpServer.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/HttpServer.java
@@ -25,6 +25,7 @@
import java.nio.charset.StandardCharsets;
import static io.ballerina.runtime.profiler.util.Constants.OUT_STREAM;
+import static io.ballerina.runtime.profiler.util.Constants.PERFORMANCE_JSON;
/**
* This class contains the HTTP server of the Ballerina profiler.
@@ -36,7 +37,7 @@ public class HttpServer {
public void initializeHTMLExport(String sourceRoot) throws IOException {
OUT_STREAM.printf(" ○ Output: " + Constants.ANSI_YELLOW +
"%s/ProfilerOutput.html" + Constants.ANSI_RESET + "%n", sourceRoot);
- String content = FileUtils.readFileAsString("performance_report.json");
+ String content = FileUtils.readFileAsString(PERFORMANCE_JSON);
FrontEnd frontEnd = new FrontEnd();
String htmlData = frontEnd.getSiteData(content);
String fileName = "ProfilerOutput.html";
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/JsonParser.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/JsonParser.java
index eaaf42d73fe3..3ab992b17baf 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/JsonParser.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/ui/JsonParser.java
@@ -27,11 +27,10 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import static io.ballerina.runtime.profiler.util.Constants.CPU_PRE_JSON;
import static io.ballerina.runtime.profiler.util.Constants.OUT_STREAM;
+import static io.ballerina.runtime.profiler.util.Constants.PERFORMANCE_JSON;
/**
* This class contains the JSON parser of the Ballerina profiler.
@@ -42,17 +41,19 @@ public class JsonParser {
private static final String VALUE_KEY = "value";
- public void initializeCPUParser(String skipFunctionString) {
- ArrayList skipList = new ArrayList<>();
- skipList = skipFunctionString != null ? parseSkipFunctionStringToList(skipFunctionString) : skipList;
- skipList.add("$gen");
- skipList.add("getAnonType");
- cpuParser(skipList);
- }
-
- private ArrayList parseSkipFunctionStringToList(String skipFunctionString) {
- String[] elements = skipFunctionString.replace("[", "").replace("]", "").split(", ");
- return new ArrayList<>(Arrays.asList(elements));
+ public void initializeCPUParser(String cpuFilePath) {
+ try {
+ String jsonInput = FileUtils.readFileAsString(cpuFilePath);
+ List input = populateStackTraceItems(jsonInput);
+ // Create a Data object to store the output
+ Data output = new Data("Root", input.get(0).time, new ArrayList<>());
+ for (StackTraceItem stackTraceItem : input) {
+ analyseStackTraceItems(stackTraceItem, output);
+ }
+ writeToValueJson(output);
+ } catch (Exception throwable) {
+ OUT_STREAM.println(throwable + "%n");
+ }
}
private int getTotalTime(JsonObject node) {
@@ -70,7 +71,7 @@ private int getTotalTime(JsonObject node) {
private void writePerformanceJson(String parsedJson) {
parsedJson = "var data = " + parsedJson;
- try (FileWriter myWriter = new FileWriter("performance_report.json", StandardCharsets.UTF_8)) {
+ try (FileWriter myWriter = new FileWriter(PERFORMANCE_JSON, StandardCharsets.UTF_8)) {
myWriter.write(parsedJson);
myWriter.flush();
} catch (IOException e) {
@@ -78,51 +79,10 @@ private void writePerformanceJson(String parsedJson) {
}
}
- private boolean containsAnySkipList(String str, List arrayList) {
- for (String s : arrayList) {
- if (str.contains(s)) {
- return true;
- }
- }
- return false;
- }
-
- private void cpuParser(ArrayList skipList) {
- try {
- String jsonInput = FileUtils.readFileAsString(CPU_PRE_JSON);
- StringBuilder jsonInputStringBuffer = new StringBuilder(jsonInput);
- if (jsonInputStringBuffer.length() > 3) {
- // Removes the trailing comma
- jsonInputStringBuffer.deleteCharAt(jsonInputStringBuffer.length() - 3);
- }
- jsonInput = jsonInputStringBuffer.toString();
- List input = populateStackTraceItems(jsonInput);
- // Create a Data object to store the output
- Data output = new Data("Root", input.get(0).time, new ArrayList<>());
- for (StackTraceItem stackTraceItem : input) {
- if (stackTraceItem.stackTrace.size() == 1) {
- output.value = Math.max(output.value, stackTraceItem.time);
- } else {
- analyseStackTraceItems(skipList, stackTraceItem, output);
- }
- }
- writeToValueJson(output);
- } catch (Exception throwable) {
- OUT_STREAM.println(throwable + "%n");
- }
- }
-
- private void analyseStackTraceItems(ArrayList skipList, StackTraceItem stackTraceItem, Data output) {
+ private void analyseStackTraceItems(StackTraceItem stackTraceItem, Data output) {
Data current = output;
for (int i = 1; i < stackTraceItem.stackTrace.size(); i++) {
- String name = stackTraceItem.stackTrace.get(i);
- if (name.contains("$configureInit()")) {
- removeChildrenByNodeName(output, name);
- break;
- }
- if (!containsAnySkipList(name, skipList)) {
- current = populateChildNodes(stackTraceItem, current, name);
- }
+ current = populateChildNodes(stackTraceItem, current, stackTraceItem.stackTrace.get(i));
}
}
@@ -136,22 +96,16 @@ private void writeToValueJson(Data output) {
writePerformanceJson(jsonObject.toString());
}
- private Data populateChildNodes(StackTraceItem stackTraceItem, Data current, String name) {
- boolean found = false;
+ private Data populateChildNodes(StackTraceItem stackTraceItem, Data current, String stackTrace) {
for (Data child : current.children) {
- if (child.name.equals(name)) {
+ if (child.name.equals(stackTrace)) {
child.value = Math.max(child.value, stackTraceItem.time);
- current = child;
- found = true;
- break;
+ return child;
}
}
- if (!found) {
- Data newChild = new Data(name, stackTraceItem.time, new ArrayList<>());
- current.children.add(newChild);
- current = newChild;
- }
- return current;
+ Data newChild = new Data(stackTrace, stackTraceItem.time, new ArrayList<>());
+ current.children.add(newChild);
+ return newChild;
}
private List populateStackTraceItems(String jsonInput) {
@@ -165,16 +119,6 @@ private List populateStackTraceItems(String jsonInput) {
return stackTraceItems;
}
- private void removeChildrenByNodeName(Data node, String nodeName) {
- if (node.name != null && node.name.equals(nodeName)) {
- node.children.clear();
- return;
- }
- for (Data child : node.children) {
- removeChildrenByNodeName(child, nodeName);
- }
- }
-
/**
* This class is used as a custom data class.
*
diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/util/Constants.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/util/Constants.java
index 5254b07dc43a..bfad3edf613e 100644
--- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/util/Constants.java
+++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/util/Constants.java
@@ -12,17 +12,28 @@ public class Constants {
public static final String ANSI_GRAY = "\033[37m";
public static final String ANSI_CYAN = "\033[1;38;2;32;182;176m";
public static final String ANSI_YELLOW = "\033[1;38;2;255;255;0m";
- public static final String TEMP_JAR_FILE_NAME = "temp.jar";
- public static final String STRAND_CLASS = "io/ballerina/runtime/internal/scheduling/Strand";
- public static final String STRAND_ARG = "(L" + STRAND_CLASS;
public static final PrintStream OUT_STREAM = System.out;
public static final PrintStream ERROR_STREAM = System.err;
public static final String CLASS_SUFFIX = ".class";
+ public static final String CPU_PRE_JSON = "cpu_pre.json";
+ public static final String PERFORMANCE_JSON = "performance_report.json";
+ public static final String TEMP_JAR_FILE_NAME = "temp.jar";
+ public static final String STRAND_PROFILER_STACK_PROPERTY = "b7a.profile.stack";
+
+ public static final String STRAND_CLASS = "io/ballerina/runtime/internal/scheduling/Strand";
+ public static final String DATA_CLASS = "io/ballerina/runtime/profiler/runtime/Data";
+ public static final String STRING_CLASS = "java/lang/String";
+ public static final String STRAND_ARG = "(L" + STRAND_CLASS;
+ public static final String START_PROFILE_DESCRIPTOR =
+ "(L" + STRAND_CLASS + ";L" + STRING_CLASS + ";L" + STRING_CLASS + ";)L" + DATA_CLASS + ";";
+ public static final String STOP_PROFILE_DESCRIPTOR =
+ "(L" + STRAND_CLASS + ";L" + DATA_CLASS + ";)V";
public static final String PROFILE_ANALYZER = "io/ballerina/runtime/profiler/runtime/ProfileAnalyzer";
public static final String GET_INSTANCE_DESCRIPTOR = "()L" + PROFILE_ANALYZER + ";";
- public static final String CPU_PRE_JSON = "cpu_pre.json";
+ public static final String CURRENT_DIR_KEY = "current.dir";
+ public static final String USER_DIR = "user.dir";
private Constants() {
}
diff --git a/bvm/ballerina-profiler/src/main/java/module-info.java b/bvm/ballerina-profiler/src/main/java/module-info.java
new file mode 100644
index 000000000000..15468516fdd5
--- /dev/null
+++ b/bvm/ballerina-profiler/src/main/java/module-info.java
@@ -0,0 +1,7 @@
+module io.ballerina.runtime.profiler {
+ requires org.objectweb.asm.commons;
+ requires org.apache.commons.io;
+ requires io.ballerina.runtime;
+ requires io.ballerina.identifier;
+ requires com.google.gson;
+}
diff --git a/bvm/ballerina-profiler/src/main/resources/profiler_output.html b/bvm/ballerina-profiler/src/main/resources/profiler_output.html
index ff6b46522d3f..104a33794a3e 100644
--- a/bvm/ballerina-profiler/src/main/resources/profiler_output.html
+++ b/bvm/ballerina-profiler/src/main/resources/profiler_output.html
@@ -40,8 +40,7 @@
}
.container {
- padding-left: 0px;
- margin-left: 40px;
+ width: 100%
}
.btn {
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
@@ -51,6 +50,7 @@
color: #1B2024;
font-weight: bold;
font-family: pragmatica, sans-serif;
+ margin-right: 10px;
}
.btn:hover {
@@ -71,10 +71,7 @@
}
.header {
- margin-left: -40px;
- box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
- padding-right: 40px;
- width: 1920px;
+ width: auto;
background-color: #fbfbfd;
padding-bottom: 25px;
}
@@ -120,14 +117,13 @@
margin-top: 40px;
margin-bottom: 40px;
max-height: 780px;
- width: 1840px;
+ width: auto;
border-radius: 20px;
overflow-y: auto;
overflow-x: auto;
background-color: #fbfbfd;
}#details{
color: black;
- padding: 20px;
position: absolute;
left: 50%;
font-weight: bold;
@@ -157,13 +153,13 @@
Search
- Reset zoom
Clear
+ Reset zoom
- Profiler
+ Profiler
@@ -176,15 +172,16 @@
${profile_data}
var flameGraph = d3.flamegraph()
- .width(1840)
- .selfValue(false)
+ .width(window.innerWidth - 40)
.cellHeight(18)
.transitionDuration(750)
.minFrameSize(5)
.transitionEase(d3.easeCubic)
- .sort(false)
+ .sort(true)
+ .title("")
.onClick(onClick)
- .differential(false);
+ .differential(false)
+ .selfValue(false);
var details = document.getElementById("details");
flameGraph.setDetailsElement(details);
diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java
index 4fce809cf3ce..652795c5b68c 100644
--- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java
+++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java
@@ -165,19 +165,10 @@ public void handleChannelError(ChannelDetails[] channels, ErrorValue error) {
}
}
- /**
- * @deprecated use Environment#getStrandLocal()
- */
- @Deprecated
public Object getProperty(String key) {
return this.globalProps.get(key);
}
- /**
- *
- * @deprecated use Environment#setStrandLocal()
- */
- @Deprecated
public void setProperty(String key, Object value) {
this.globalProps.put(key, value);
}
diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java
index 0f0f32d58a1c..edcfdf0722c7 100644
--- a/bvm/ballerina-runtime/src/main/java/module-info.java
+++ b/bvm/ballerina-runtime/src/main/java/module-info.java
@@ -43,7 +43,7 @@
io.ballerina.lang.table, io.ballerina.lang.value, io.ballerina.lang.xml, ballerina.debug.adapter.core,
io.ballerina.cli, io.ballerina.lang.integer, io.ballerina.lang.bool, io.ballerina.lang.decimal,
io.ballerina.lang.floatingpoint, io.ballerina.lang.internal, io.ballerina.lang.function,
- io.ballerina.lang.regexp;
+ io.ballerina.lang.regexp, io.ballerina.runtime.profiler;
exports io.ballerina.runtime.internal.commons to io.ballerina.lang.value;
exports io.ballerina.runtime.internal.launch to io.ballerina.testerina.runtime, io.ballerina.packerina,
ballerina.test.listener, io.ballerina.cli, org.ballerinalang.debugadapter.runtime;
@@ -51,7 +51,7 @@
io.ballerina.lang.array, io.ballerina.lang.error, io.ballerina.lang.internal, io.ballerina.lang.map,
io.ballerina.lang.table, io.ballerina.lang.transaction, io.ballerina.lang.value, io.ballerina.lang.xml,
io.ballerina.log.api, io.ballerina.testerina.core, io.ballerina.testerina.runtime, io.ballerina.shell,
- org.ballerinalang.debugadapter.runtime, io.ballerina.lang.function;
+ org.ballerinalang.debugadapter.runtime, io.ballerina.lang.function, io.ballerina.runtime.profiler;
exports io.ballerina.runtime.internal.util to io.ballerina.testerina.runtime, io.ballerina.lang,
io.ballerina.lang.integer, io.ballerina.lang.floatingpoint, io.ballerina.lang.array,
io.ballerina.lang.table, io.ballerina.java, io.ballerina.lang.map, io.ballerina.lang.string,
diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java
index 6a867816cf4a..f6e562fde697 100644
--- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java
+++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java
@@ -50,6 +50,7 @@
public class RunProfilerTask implements Task {
private final PrintStream err;
private static final String JAVA_OPTS = "JAVA_OPTS";
+ private static final String CURRENT_DIR_KEY = "current.dir";
public RunProfilerTask(PrintStream errStream) {
this.err = errStream;
@@ -76,14 +77,15 @@ private void initiateProfiler(Project project) {
commands.add(getPackageJarName(project, projectKind));
commands.add("--target");
commands.add(targetPath.toString());
- commands.add("--sourceroot");
+ commands.add("--source-root");
commands.add(getProjectPath(project).toString());
if (isInProfileDebugMode()) {
- commands.add("--profilerDebug");
+ commands.add("--profiler-debug");
commands.add(getProfileDebugArg(err));
}
ProcessBuilder pb = new ProcessBuilder(commands).inheritIO();
pb.environment().put(JAVA_OPTS, getAgentArgs());
+ pb.environment().put(CURRENT_DIR_KEY, System.getProperty(USER_DIR));
setWorkingDirectory(project, projectKind, pb);
Process process = pb.start();
process.waitFor();
diff --git a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ProfileCommandTest.java b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ProfileCommandTest.java
index a1b6a401590e..57f73b3656a9 100644
--- a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ProfileCommandTest.java
+++ b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ProfileCommandTest.java
@@ -85,8 +85,8 @@ public void testRunBalProjectWithProfileFlag() throws IOException {
Assert.assertTrue(htmlPath.toFile().exists());
try {
String htmlContent = Files.readString(htmlPath);
- Assert.assertTrue(htmlContent.contains("foo/package_a/0/main.main()"));
- Assert.assertTrue(htmlContent.contains("foo/package_a/0/$_init.$moduleExecute()"));
+ Assert.assertTrue(htmlContent.contains("foo/package_a/0/main.main"));
+ Assert.assertTrue(htmlContent.contains("foo/package_a/0/$_init.$moduleInit"));
} catch (IOException e) {
Assert.fail("Error reading html file");
}
diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/profiler/ProfilerTest.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/profiler/ProfilerTest.java
index 059bb79fc497..a5f3ae08293c 100644
--- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/profiler/ProfilerTest.java
+++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/profiler/ProfilerTest.java
@@ -25,6 +25,7 @@
import org.ballerinalang.test.context.ServerLogReader;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import org.wso2.ballerinalang.util.Lists;
import java.io.IOException;
import java.nio.file.Paths;
@@ -51,20 +52,36 @@ public void setup() throws BallerinaTestException {
@Test
public void testProfilerExecutionWithBalPackage() throws BallerinaTestException {
- String sourceRoot = testFileLocation + "/";
String packageName = "projectForProfile/package_a";
+ String sourceRoot = testFileLocation + "/";
Map envProperties = new HashMap<>();
bMainInstance.addJavaAgents(envProperties);
- LogLeecher[] leechers = getProfilerLogLeechers(packageName + "/target/bin/" + outputFile);
- bMainInstance.runMain("profile", new String[]{packageName}, envProperties,
- null, leechers, sourceRoot);
+ List leechers = getProfilerLogLeechers(packageName + "/target/bin/" + outputFile);
+ leechers.add(new LogLeecher("Is the array sorted? true"));
+ bMainInstance.runMain("profile", new String[]{packageName}, envProperties, null,
+ leechers.toArray(new LogLeecher[0]), sourceRoot);
for (LogLeecher leecher : leechers) {
leecher.waitForText(5000);
}
}
- private LogLeecher[] getProfilerLogLeechers(String htmlFilePath) {
- return new LogLeecher[]{
+ @Test
+ public void testProfilerExecutionWithConfigurableVars() throws BallerinaTestException {
+ String packageName = "projectForProfile/package_b";
+ String sourceRoot = testFileLocation + "/" + packageName;
+ Map envProperties = new HashMap<>();
+ bMainInstance.addJavaAgents(envProperties);
+ List leechers = getProfilerLogLeechers(packageName + "/target/bin/" + outputFile);
+ leechers.add(new LogLeecher("Tests passed"));
+ bMainInstance.runMain("profile", new String[]{}, envProperties, null,
+ leechers.toArray(new LogLeecher[0]), sourceRoot);
+ for (LogLeecher leecher : leechers) {
+ leecher.waitForText(5000);
+ }
+ }
+
+ private List getProfilerLogLeechers(String htmlFilePath) {
+ return Lists.of(
new LogLeecher("[1/6] Initializing..."),
new LogLeecher("[2/6] Copying executable..."),
new LogLeecher("[3/6] Performing analysis..."),
@@ -73,9 +90,9 @@ private LogLeecher[] getProfilerLogLeechers(String htmlFilePath) {
new LogLeecher("○ Instrumented function count: "),
new LogLeecher("[5/6] Running executable..."),
new LogLeecher("[6/6] Generating output..."),
- new LogLeecher("○ Execution time:"),
+ new LogLeecher(" ○ Execution time: [1-5] seconds ", true, LogLeecher.LeecherType.INFO),
new LogLeecher("○ Output: "),
- new LogLeecher(htmlFilePath)};
+ new LogLeecher(htmlFilePath));
}
@Test
@@ -84,9 +101,9 @@ public void testProfilerExecutionWithSingleBalFile() throws BallerinaTestExcepti
String fileName = "profiler_single_file.bal";
Map envProperties = new HashMap<>();
bMainInstance.addJavaAgents(envProperties);
- LogLeecher[] leechers = getProfilerLogLeechers(sourceRoot + outputFile);
+ List leechers = getProfilerLogLeechers(sourceRoot + outputFile);
bMainInstance.runMain("profile", new String[]{fileName}, envProperties,
- null, leechers, sourceRoot);
+ null, leechers.toArray(new LogLeecher[0]), sourceRoot);
for (LogLeecher leecher : leechers) {
leecher.waitForText(5000);
}
diff --git a/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_a/main.bal b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_a/main.bal
index e75aa457e040..08425f16effa 100644
--- a/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_a/main.bal
+++ b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_a/main.bal
@@ -15,7 +15,68 @@
// under the License.
import foo/package_a.mod_a1;
+import ballerina/jballerina.java;
-public function main() {
+public function main() returns error? {
mod_a1:func1();
+ int[] arr = createRandomIntArray(check float:pow(10,3).cloneWithType(int));
+ int[] sortedArr = bubbleSort(arr);
+ boolean isSorted = isSortedArray(sortedArr);
+ print("Is the array sorted? " + isSorted.toString());
}
+
+public isolated function bubbleSort(int[] arr) returns int[] {
+ int n = arr.length();
+ int temp = 0;
+ boolean swapped = false;
+ foreach int i in 0 ... n - 2 {
+ foreach int j in 1 ... n - 1 - i {
+ if (arr[j - 1] > arr[j]) {
+ temp = arr[j - 1];
+ arr[j - 1] = arr[j];
+ arr[j] = temp;
+ swapped = true;
+ }
+ }
+ if (!swapped) {
+ break;
+ }
+ }
+ return arr;
+}
+
+isolated function isSortedArray(int[] sortedArr) returns boolean {
+ foreach int i in 0 ..< sortedArr.length() - 1 {
+ if (sortedArr[i] > sortedArr[i + 1]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+isolated function createRandomIntArray(int size) returns int[] {
+ int[] array = [];
+ int count = 0;
+ foreach int i in 0 ..< size {
+ array.push(count);
+ count += 1;
+ }
+ return array;
+}
+
+function print(string value) {
+ handle strValue = java:fromString(value);
+ handle stdout1 = stdout();
+ printInternal(stdout1, strValue);
+}
+
+public function stdout() returns handle = @java:FieldGet {
+ name: "out",
+ 'class: "java/lang/System"
+} external;
+
+public function printInternal(handle receiver, handle strValue) = @java:Method {
+ name: "println",
+ 'class: "java/io/PrintStream",
+ paramTypes: ["java.lang.String"]
+} external;
diff --git a/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/Ballerina.toml b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/Ballerina.toml
new file mode 100644
index 000000000000..5cfbc6a5d346
--- /dev/null
+++ b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/Ballerina.toml
@@ -0,0 +1,4 @@
+[package]
+org = "foo"
+name = "package_b"
+version = "0.1.0"
diff --git a/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/Config.toml b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/Config.toml
new file mode 100644
index 000000000000..bff5108ff9af
--- /dev/null
+++ b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/Config.toml
@@ -0,0 +1 @@
+intValue = 10
diff --git a/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/main.bal b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/main.bal
new file mode 100644
index 000000000000..67ca87f0b8f3
--- /dev/null
+++ b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/main.bal
@@ -0,0 +1,44 @@
+// Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+//
+// WSO2 LLC. 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.
+
+import foo/package_b.mod_a1;
+import ballerina/test;
+import ballerina/jballerina.java;
+
+configurable int intValue = ?;
+
+public function main() {
+ int a = mod_a1:func1() + intValue;
+ test:assertEquals(a, 30);
+ print("Tests passed");
+}
+
+function print(string value) {
+ handle strValue = java:fromString(value);
+ handle stdout1 = stdout();
+ printInternal(stdout1, strValue);
+}
+
+public function stdout() returns handle = @java:FieldGet {
+ name: "out",
+ 'class: "java/lang/System"
+} external;
+
+public function printInternal(handle receiver, handle strValue) = @java:Method {
+ name: "println",
+ 'class: "java/io/PrintStream",
+ paramTypes: ["java.lang.String"]
+} external;
diff --git a/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/modules/mod_a1/mod1.bal b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/modules/mod_a1/mod1.bal
new file mode 100644
index 000000000000..24e91d230daf
--- /dev/null
+++ b/tests/jballerina-integration-test/src/test/resources/profiler/projectForProfile/package_b/modules/mod_a1/mod1.bal
@@ -0,0 +1,27 @@
+// Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+//
+// WSO2 LLC. 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.
+
+boolean num = float:isFinite(1.0);
+int a = 10;
+
+public function func1() returns int {
+ if (num) {
+ a = 20;
+ } else {
+ a = 30;
+ }
+ return a;
+}