* [PATCH/RFC] java: add initial bindings
@ 2022-06-20 20:45 Alexander Aring
2022-07-08 18:30 ` Steven Rostedt
0 siblings, 1 reply; 3+ messages in thread
From: Alexander Aring @ 2022-06-20 20:45 UTC (permalink / raw)
To: linux-trace-devel; +Cc: cluster-devel, aahringo
This patch adds basic java bindings for libtracecmd. It currently
supports very basic handling of parsing trace-cmd recorded trace files.
There might of course additional improvements regarding swig bindings and
the memory handling. E.g. in javas builtin iterator for-each handling
everything will keep in memory. The license for the high-level java
bindings jar file is LGPL-2.1 and is the same as libtracecmd.
The author of this patch created a java application that uses those java
bindings to trace locks and represent them in graphical GANTT diagram,
see [0].
You need to set the JAVA_HOME environment variable to let the Makefile know
that it builds the java bindings. This is somehow standard in the java world
as replacement for pkg-config or similar. There should no trace-cmd java
dependency, the recommended way should be to provide a kind of trace-cmd-java
package from your distribution containing the tracecmd.jar and
libctracecmdjava.so. This package would have then a java dependency to
e.g. OpenJDK, that I was using to test those bindings for.
The author is not a swig expert but it works as it currently is. Also
the author did not hit issues because memory _yet_. Those are beginning
experimental bindings and can be changed/improved in future.
[0] https://gitlab.com/netcoder/dlm2slog2
Signed-off-by: Alexander Aring <aahringo@redhat.com>
---
.gitignore | 4 +
Makefile | 60 ++++++-
java/Makefile | 39 +++++
java/TraceCmd.java | 236 +++++++++++++++++++++++++
java/TraceCmdEvent.java | 277 ++++++++++++++++++++++++++++++
java/TraceCmdException.java | 16 ++
java/TraceCmdField.java | 104 +++++++++++
java/ctracecmdjava.i | 180 +++++++++++++++++++
java/example/Makefile | 7 +
java/example/TraceCmdExample.java | 33 ++++
10 files changed, 953 insertions(+), 3 deletions(-)
create mode 100644 java/Makefile
create mode 100644 java/TraceCmd.java
create mode 100644 java/TraceCmdEvent.java
create mode 100644 java/TraceCmdException.java
create mode 100644 java/TraceCmdField.java
create mode 100644 java/ctracecmdjava.i
create mode 100644 java/example/Makefile
create mode 100644 java/example/TraceCmdExample.java
diff --git a/.gitignore b/.gitignore
index eb1b0db..586601a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,12 +10,16 @@
.pc
*~
*.pyc
+*.class
+*.jar
*.swp
\#*\#
patches/
tc_version.h
ks_version.h
ctracecmd_wrap.c
+ctracecmdjava_wrap.c
+swig/
ctracecmdgui_wrap.c
tags
TAGS
diff --git a/Makefile b/Makefile
index d72686d..eeafac9 100644
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,7 @@ export DESTDIR DESTDIR_SQ
ifeq ($(prefix),$(HOME))
plugin_tracecmd_dir = $(libdir)/trace-cmd/plugins
python_dir ?= $(libdir)/trace-cmd/python
+java_dir ?= $(libdir)/trace-cmd/java
var_dir = $(HOME)/.trace-cmd/
else
python_dir ?= $(libdir)/trace-cmd/python
@@ -96,6 +97,9 @@ PLUGIN_DIR_TRACECMD = -DPLUGIN_TRACECMD_DIR="$(plugin_tracecmd_dir)"
PYTHON_DIR = -DPYTHON_DIR="$(python_dir)"
PLUGIN_DIR_TRACECMD_SQ = '$(subst ','\'',$(PLUGIN_DIR_TRACECMD))'
PYTHON_DIR_SQ = '$(subst ','\'',$(PYTHON_DIR))'
+java_dir ?= $(libdir)/trace-cmd/java
+JAVA_DIR = -DJAVA_DIR="$(java_dir)"
+JAVA_DIR_JAR = '$(subst ','\'',$(JAVA_DIR))'
var_dir = /var
endif
@@ -104,6 +108,7 @@ bindir_SQ = $(subst ','\'',$(bindir))
bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
plugin_tracecmd_dir_SQ = $(subst ','\'',$(plugin_tracecmd_dir))
python_dir_SQ = $(subst ','\'',$(python_dir))
+java_dir_JAR = $(subst ','\'',$(java_dir))
pound := \#
@@ -120,8 +125,11 @@ BASH_COMPLETE_DIR ?= $(etcdir)/bash_completion.d
export PLUGIN_DIR_TRACECMD
export PYTHON_DIR
export PYTHON_DIR_SQ
+export JAVA_DIR
+export JAVA_DIR_JAR
export plugin_tracecmd_dir_SQ
export python_dir_SQ
+export java_dir_JAR
export var_dir
# copy a bit from Linux kbuild
@@ -139,6 +147,7 @@ SWIG_DEFINED := $(shell if command -v swig; then echo 1; else echo 0; fi)
ifeq ($(SWIG_DEFINED), 0)
BUILD_PYTHON := report_noswig
NO_PYTHON = 1
+NO_JAVA = 1
endif
ifndef NO_PYTHON
@@ -160,6 +169,21 @@ endif # NO_PYTHON
export BUILD_PYTHON_WORKS
export NO_PYTHON
+ifndef NO_JAVA
+JAVA := tracecmd.jar
+
+ifeq ($(origin JAVA_HOME),undefined)
+ BUILD_JAVA := report_nojavadev
+ NO_JAVA = 1
+else
+ BUILD_JAVA := $(JAVA)
+ BUILD_JAVA_WORKS := 1
+endif
+endif # NO_JAVA
+
+export BUILD_JAVA_WORKS
+export NO_JAVA
+
# $(call test-build, snippet, ret) -> ret if snippet compiles
# -> empty otherwise
test-build = $(if $(shell sh -c 'echo "$(1)" | \
@@ -375,7 +399,7 @@ override CFLAGS += $(PLUGIN_DIR_TRACECMD_SQ)
override CFLAGS += $(udis86-flags) $(blk-flags) $(memfd-flags)
override LDFLAGS += $(udis86-ldflags)
-CMD_TARGETS = trace-cmd $(BUILD_PYTHON)
+CMD_TARGETS = trace-cmd $(BUILD_PYTHON) $(BUILD_JAVA)
###
# Default we just build trace-cmd
@@ -487,10 +511,13 @@ install_plugins: install_plugins_tracecmd
install_python: force
$(Q)$(MAKE) -C $(src)/python $@
+install_java: force
+ $(Q)$(MAKE) -C $(src)/java $@
+
install_bash_completion: force
$(Q)$(call do_install_data,$(src)/tracecmd/trace-cmd.bash,$(BASH_COMPLETE_DIR))
-install_cmd: all_cmd install_plugins install_python install_bash_completion
+install_cmd: all_cmd install_plugins install_python install_java install_bash_completion
$(Q)$(call do_install,$(obj)/tracecmd/trace-cmd,$(bindir_SQ))
install: install_cmd
@@ -519,6 +546,7 @@ clean:
$(MAKE) -C $(src)/lib/trace-cmd/plugins clean
$(MAKE) -C $(src)/utest clean
$(MAKE) -C $(src)/python clean
+ $(MAKE) -C $(src)/java clean
$(MAKE) -C $(src)/tracecmd clean
define build_uninstall_script
@@ -554,7 +582,7 @@ uninstall_libs: $(BUILD_OUTPUT)/build_libs_uninstall
report_noswig: force
$(Q)echo
- $(Q)echo " NO_PYTHON forced: swig not installed, not compiling python plugins"
+ $(Q)echo " NO_PYTHON forced: swig not installed, not compiling python plugins or java bindings"
$(Q)echo
report_nopythondev: force
@@ -562,6 +590,13 @@ report_nopythondev: force
$(Q)echo " python-dev is not installed, not compiling python plugins"
$(Q)echo
+##### JAVA STUFF #####
+
+report_nojavadev: force
+ $(Q)echo
+ $(Q)echo " JAVA_HOME env is not set, not compiling java bindings"
+ $(Q)echo
+
ifndef NO_PYTHON
PYTHON_INCLUDES = `$(PKG_CONFIG) --cflags $(PYTHON_PKGCONFIG_VERS)`
PYTHON_LDFLAGS = `$(PKG_CONFIG) --libs $(PYTHON_PKGCONFIG_VERS)` \
@@ -583,6 +618,25 @@ ctracecmd.so: force $(LIBTRACECMD_STATIC)
PHONY += python
python: $(PYTHON)
+ifndef NO_JAVA
+JAVA_JAVAC = $(JAVA_HOME)/bin/javac
+JAVA_JAR = $(JAVA_HOME)/bin/jar
+JAVA_INCLUDES = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
+else
+JAVA_JAVAC =
+JAVA_JAR =
+JAVA_INCLUDES =
+endif
+
+export JAVA_JAVAC
+export JAVA_JAR
+export JAVA_INCLUDES
+
+tracecmd.jar: force $(LIBTRACECMD_STATIC)
+ $(Q)$(MAKE) -C $(src)/java $@
+
+PHONY += java
+java: $(JAVA)
dist:
git archive --format=tar --prefix=trace-cmd-$(TRACECMD_VERSION)/ HEAD \
diff --git a/java/Makefile b/java/Makefile
new file mode 100644
index 0000000..7f34c9f
--- /dev/null
+++ b/java/Makefile
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0
+
+include $(src)/scripts/utils.mk
+
+ifdef BUILD_JAVA_WORKS
+JAVA_SO_INSTALL := libctracecmdjava.install
+JAVA_JAR_INSTALL := tracecmd.install
+endif
+
+root_pkgdir := tracecmd
+swigdir := swig
+jardir := jar
+
+SRCS := TraceCmd.java TraceCmdEvent.java TraceCmdException.java TraceCmdField.java
+
+libctracecmdjava.so: ctracecmdjava.i $(LIBTRACECMD_STATIC)
+ @mkdir -p $(swigdir)
+ swig -Wall -java -noproxy -package $(root_pkgdir).swig -I$(src)/include/trace-cmd $(LIBTRACEEVENT_CFLAGS) -outdir $(swigdir) ctracecmdjava.i
+ $(CC) -fpic -c $(JAVA_INCLUDES) $(CPPFLAGS) $(CFLAGS) ctracecmdjava_wrap.c
+ $(CC) --shared $(LIBTRACECMD_STATIC) $(LDFLAGS) ctracecmdjava_wrap.o -o libctracecmdjava.so $(TRACE_LIBS)
+
+tracecmd.jar: libctracecmdjava.so $(SRCS)
+ $(JAVA_JAVAC) -d . $(swigdir)/*.java *.java
+ $(JAVA_JAR) cf tracecmd.jar $(root_pkgdir)
+
+$(JAVA_SO_INSTALL): %.install : %.so force
+ $(Q)$(call do_install_data,$<,$(libdir_SQ))
+
+$(JAVA_JAR_INSTALL): %.install : %.jar force
+ $(Q)$(call do_install_data,$<,$(java_dir_JAR))
+
+install_java: $(JAVA_SO_INSTALL) $(JAVA_JAR_INSTALL)
+
+clean:
+ $(RM) *.jar *.a *.so *.o .*.d ctracecmdjava_wrap.*
+ $(RM) -r $(swigdir) tracecmd
+
+force:
+.PHONY: clean force
diff --git a/java/TraceCmd.java b/java/TraceCmd.java
new file mode 100644
index 0000000..cb24579
--- /dev/null
+++ b/java/TraceCmd.java
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Red Hat Inc, Alexander Aring <aahringo@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+package tracecmd;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import tracecmd.swig.*;
+
+/**
+* Represents a trace instance of a trace file.
+*
+* @author Alexander Aring <aahringo@redhat.com>
+*
+*/
+public class TraceCmd implements Iterable<TraceCmdEvent>, AutoCloseable
+{
+ static { System.loadLibrary("ctracecmdjava"); }
+
+ private SWIGTYPE_p_tracecmd_input handle;
+ private SWIGTYPE_p_tep_handle pevent;
+ private List<TraceCmdEvent> events;
+ private BigInteger first_ts;
+ private Integer long_size;
+ private Integer cpus;
+
+ /**
+ * Instantiate a TraceCmd Object to read a tracefile
+ *
+ * @param file relative or absolute filepath to the tracefile to read
+ */
+ public TraceCmd(String file)
+ {
+ int rv;
+
+ this.handle = CTraceCmd.tracecmd_alloc(file, 0);
+ if (this.handle == null)
+ throw new TraceCmdException("Failed to alloc file: " + file);
+
+ /* 0 is TRACECMD_FILE_ALLOCATED */
+ rv = CTraceCmd.tracecmd_read_headers(this.handle, 0);
+ if (rv != 0)
+ throw new TraceCmdException("Failed to read headers of file: " + file);
+
+ rv = CTraceCmd.tracecmd_init_data(this.handle);
+ if (rv != 0)
+ throw new TraceCmdException("Failed to init data of file: " + file);
+
+ this.pevent = CTraceCmd.tracecmd_get_tep(this.handle);
+ }
+
+ /**
+ * Get the number of CPUs recorded
+ *
+ * @return the number of CPUs recorded
+ */
+ public Integer getCpus()
+ {
+ if (this.cpus != null)
+ return this.cpus;
+
+ this.cpus = CTraceCmd.tracecmd_cpus(this.handle);
+ return this.cpus;
+ }
+
+ /**
+ * Get the size of a long for the recorded tracefile
+ *
+ * @return the size of a long type
+ */
+ public Integer getLongSize()
+ {
+ if (this.long_size != null)
+ return this.long_size;
+
+ this.long_size = CTraceCmd.tracecmd_long_size(this.handle);
+ return this.long_size;
+ }
+
+ /**
+ * Get the first recorded timestamp
+ *
+ * @return the first recorded timestamp
+ */
+ public BigInteger getFirstTs()
+ {
+ if (this.first_ts != null)
+ return this.first_ts;
+
+ this.first_ts = CTraceCmd.tracecmd_get_first_ts(this.handle);
+ return this.first_ts;
+ }
+
+ /**
+ * Read the next record and increment
+ *
+ * Returns null if end reached.
+ *
+ * @param cpu the CPU to pull from
+ * @return the specifc event as a TraceCmdEvent object
+ */
+ public TraceCmdEvent getEvent(Integer cpu)
+ {
+ SWIGTYPE_p_tep_event format;
+ SWIGTYPE_p_tep_record rec;
+ int type;
+
+ rec = CTraceCmd.tracecmd_read_data(this.handle, cpu);
+ if (rec == null)
+ return null;
+
+ type = CTraceCmd.tep_data_type(this.pevent, rec);
+ format = CTraceCmd.tep_find_event(this.pevent, type);
+
+ return new TraceCmdEvent(this.pevent, rec, format);
+ }
+
+ /**
+ * Read a record from a specific offset
+ *
+ * @param offset the offset into the file to find the record
+ * @return the specifc event as a TraceCmdEvent object
+ */
+ public TraceCmdEvent getEventAt(BigInteger offset) throws TraceCmdException
+ {
+ SWIGTYPE_p_tep_event format;
+ SWIGTYPE_p_tep_record rec;
+ int type;
+
+ rec = CTraceCmd.tracecmd_read_at(this.handle, offset, null);
+ if (rec == null)
+ throw new TraceCmdException("Failed to read data at: " + offset);
+
+ type = CTraceCmd.tep_data_type(this.pevent, rec);
+ format = CTraceCmd.tep_find_event(this.pevent, type);
+
+ return new TraceCmdEvent(this.pevent, rec, format);
+ }
+
+ /**
+ * Read the next record on any cpu.
+ *
+ * Returns null if end reached.
+ *
+ * @return the specifc event as a TraceCmdEvent object
+ */
+ public TraceCmdEvent getNextEvent()
+ {
+ SWIGTYPE_p_tep_event format;
+ SWIGTYPE_p_tep_record rec;
+ int[] rec_cpu = { 0 };
+ int type;
+
+ rec = CTraceCmd.tracecmd_read_next_data(this.handle, rec_cpu);
+ if (rec == null)
+ return null;
+
+ type = CTraceCmd.tep_data_type(this.pevent, rec);
+ format = CTraceCmd.tep_find_event(this.pevent, type);
+
+ return new TraceCmdEvent(this.pevent, rec, format);
+ }
+
+
+ /**
+ * Return the record at the current location by cpu iterator.
+ *
+ * Returns null if end reached.
+ *
+ * @param cpu the CPU to pull from
+ * @return the specifc event as a TraceCmdEvent object
+ */
+ public TraceCmdEvent peekEvent(Integer cpu)
+ {
+ SWIGTYPE_p_tep_event format;
+ SWIGTYPE_p_tep_record rec;
+ int type;
+
+ rec = CTraceCmd.tracecmd_peek_data_ref(this.handle, cpu);
+ if (rec == null)
+ return null;
+
+ type = CTraceCmd.tep_data_type(this.pevent, rec);
+ format = CTraceCmd.tep_find_event(this.pevent, type);
+
+ return new TraceCmdEvent(this.pevent, rec, format);
+ }
+
+ /**
+ * Closes the TraceCmd handle.
+ *
+ * If not called AutoCloseable will take care about it.
+ */
+ @Override
+ public void close()
+ {
+ CTraceCmd.tracecmd_close(this.handle);
+ }
+
+ /**
+ * Iterator over all Events by using getNextEvent().
+ *
+ * Probably the best and easiest way to multiple times iterate
+ * over all events in order of their timestamp. Note that all
+ * events will be held in memory of TraceCmd lifetime.
+ *
+ * Just use for-each loop e.g. for (TraceCmdEvent e:new TraceCmd(...))
+ *
+ * @return iterator object
+ */
+ @Override
+ public Iterator<TraceCmdEvent> iterator()
+ {
+ if (this.events == null) {
+ TraceCmdEvent e;
+
+ CTraceCmd.tracecmd_set_all_cpus_to_timestamp(this.handle, this.getFirstTs());
+ this.events = new LinkedList<TraceCmdEvent>();
+ e = this.getNextEvent();
+ while (e != null) {
+ this.events.add(e);
+ e = this.getNextEvent();
+ }
+ }
+
+ return this.events.listIterator();
+ }
+}
diff --git a/java/TraceCmdEvent.java b/java/TraceCmdEvent.java
new file mode 100644
index 0000000..ff8a12d
--- /dev/null
+++ b/java/TraceCmdEvent.java
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Red Hat Inc, Alexander Aring <aahringo@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+package tracecmd;
+
+import java.lang.ref.Cleaner;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import tracecmd.swig.*;
+
+/**
+* TraceCmdEvent represent a trace event.
+*
+* @author Alexander Aring <aahringo@redhat.com>
+*
+*/
+public class TraceCmdEvent implements AutoCloseable
+{
+ /* memory free handler */
+ private static final Cleaner cleaner = Cleaner.create();
+ private final Cleaner.Cleanable cleanable;
+ private final TraceCmdEventCleaner ec;
+
+ /* swig handlers */
+ private SWIGTYPE_p_tep_handle pevent;
+ private SWIGTYPE_p_tep_record record;
+ private SWIGTYPE_p_tep_event format;
+
+ /* caches attributes */
+ private Set<String> keys;
+ private BigInteger ts;
+ private String system;
+ private Integer type;
+ private String name;
+ private Integer cpu;
+ private String comm;
+ private Integer pid;
+
+ static class TraceCmdEventCleaner implements Runnable {
+ private SWIGTYPE_p_tep_record record;
+
+ public TraceCmdEventCleaner(SWIGTYPE_p_tep_record record) {
+ this.record = record;
+ }
+
+ @Override
+ public void run(){
+ CTraceCmd.tracecmd_free_record(this.record);
+ }
+ }
+
+ public TraceCmdEvent(SWIGTYPE_p_tep_handle pevent,
+ SWIGTYPE_p_tep_record record,
+ SWIGTYPE_p_tep_event format)
+ {
+ this.pevent = pevent;
+ this.record = record;
+ this.format = format;
+
+ this.ec = new TraceCmdEventCleaner(record);
+ this.cleanable = cleaner.register(this, this.ec);
+ }
+
+ /**
+ * Freeing up a TraceCmdEvent object.
+ *
+ * If not called AutoCloseable will take care about it.
+ */
+ @Override
+ public void close()
+ {
+ cleanable.clean();
+ }
+
+ /**
+ * Get the trace system name of the trace event.
+ *
+ * @return the specific trace system as string
+ */
+ public String getSystem()
+ {
+ if (this.system != null)
+ return this.system;
+
+ this.system = CTraceCmd.tracecmd_event_system_get(this.format);
+ return this.system;
+ }
+
+ /**
+ * Get the trace event name.
+ *
+ * @return the specific trace event name as string
+ */
+ public String getName()
+ {
+ if (this.name != null)
+ return this.name;
+
+ this.name = CTraceCmd.tracecmd_event_name_get(this.format);
+ return this.name;
+ }
+
+ /**
+ * Get the trace event timestamp.
+ *
+ * @return the specific trace event timestamp in uptime nanoseconds.
+ */
+ public BigInteger getTs()
+ {
+ if (this.ts != null)
+ return this.ts;
+
+ this.ts = CTraceCmd.tracecmd_record_ts_get(this.record);
+ return this.ts;
+ }
+
+ /**
+ * Get the cpu on which the event was recorded on.
+ *
+ * @return the specific cpu which the event was record on.
+ */
+ public Integer getCpu()
+ {
+ if (this.cpu != null)
+ return this.cpu;
+
+ this.cpu = CTraceCmd.tracecmd_record_cpu_get(this.record);
+ return this.cpu;
+ }
+
+ /**
+ * Get the pid on which the event was recorded on.
+ *
+ * @return the specific pid which the event was record on.
+ */
+ public Integer getPid()
+ {
+ if (this.pid != null)
+ return this.pid;
+
+ this.pid = CTraceCmd.tep_data_pid(this.pevent, this.record);
+ return this.pid;
+ }
+
+ public Integer getType()
+ {
+ if (this.type != null)
+ return this.type;
+
+ this.type = CTraceCmd.tep_data_type(this.pevent, this.record);
+ return this.type;
+ }
+
+ public String getComm()
+ {
+ if (this.comm != null)
+ return this.comm;
+
+ this.comm = CTraceCmd.tep_data_comm_from_pid(this.pevent, this.getPid());
+ return this.comm;
+ }
+
+ /**
+ * Get a set of available keys for getField().
+ *
+ * @return a String set for specific keys.
+ */
+ public Set<String> getKeys()
+ {
+ HashSet<String> ret = new HashSet<String>();
+ SWIGTYPE_p_tep_format_field f;
+
+ if (this.keys != null)
+ return this.keys;
+
+ f = CTraceCmd.tracecmd_event_format_fields_get(this.format);
+ while (f != null) {
+ ret.add(CTraceCmd.tracecmd_field_name_get(f));
+ f = CTraceCmd.tracecmd_field_next_get(f);
+ }
+
+ this.keys = ret;
+ return this.keys;
+ }
+
+ public List<String> getStack(Integer long_size) throws TraceCmdException
+ {
+ List<String> ret = new ArrayList<String>();
+ long[] addr = { 0 };
+ SWIGTYPE_p_void i;
+ String str;
+ int rv;
+
+ i = CTraceCmd.java_field_get_stack_init(this.record, this.format);
+ if (i == null)
+ throw new TraceCmdException("Failed to get stack data");
+
+ for (;;) {
+ rv = CTraceCmd.java_field_get_stack_check(this.record,
+ this.format,
+ i, long_size,
+ addr);
+ if (rv == 1)
+ break;
+
+ str = CTraceCmd.java_field_get_stack_str(this.format, addr[0]);
+ if (str == null)
+ throw new TraceCmdException("Failed to get stack string");
+
+ ret.add(str);
+ i = CTraceCmd.java_field_get_stack_next(i, long_size);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Get a TraceCmdField object for a specific trace field.
+ *
+ * @param name the name of the field.
+ * @return a TraceCmdField object which represent the event field.
+ */
+ public TraceCmdField getField(String name) throws TraceCmdException
+ {
+ SWIGTYPE_p_tep_format_field f = CTraceCmd.tep_find_field(this.format, name);
+ if (f == null)
+ throw new TraceCmdException("Field not found: " + name);
+
+ return new TraceCmdField(this.record, f);
+ }
+
+ /**
+ * Get a number value of a specific trace field.
+ *
+ * @param name the name of the field.
+ * @return the number value.
+ */
+ public BigInteger getNumField(String name)
+ {
+ return this.getField(name).getNum();
+ }
+
+ /**
+ * Get a string value of a specific trace field.
+ *
+ * @param name the name of the field.
+ * @return the string value.
+ */
+ public String getStrField(String name)
+ {
+ return this.getField(name).toString();
+ }
+
+ /**
+ * Get a string representation of the trace event.
+ *
+ * @param name the name of the field.
+ * @return the string representation.
+ */
+ @Override
+ public String toString()
+ {
+ BigInteger[] ts = this.getTs().divideAndRemainder(new BigInteger("1000000000"));
+
+ return String.format("%d.%09d CPU%d %s: pid=%d comm=%s type=%d",
+ ts[0], ts[1], this.getCpu(), this.getName(),
+ this.getPid(), this.getComm(), this.getType());
+ }
+}
diff --git a/java/TraceCmdException.java b/java/TraceCmdException.java
new file mode 100644
index 0000000..f17ac5d
--- /dev/null
+++ b/java/TraceCmdException.java
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Red Hat Inc, Alexander Aring <aahringo@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+package tracecmd;
+
+class TraceCmdException extends RuntimeException
+{
+ public TraceCmdException(String m)
+ {
+ super(m);
+ }
+}
diff --git a/java/TraceCmdField.java b/java/TraceCmdField.java
new file mode 100644
index 0000000..82079c4
--- /dev/null
+++ b/java/TraceCmdField.java
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Red Hat Inc, Alexander Aring <aahringo@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+package tracecmd;
+
+import java.math.BigInteger;
+
+import tracecmd.swig.*;
+
+/**
+* TraceCmdEventField represent a field of an trace event.
+*
+* @author Alexander Aring <aahringo@redhat.com>
+*
+*/
+public class TraceCmdField
+{
+ private SWIGTYPE_p_tep_format_field field;
+ private SWIGTYPE_p_tep_record record;
+
+ private Byte[] data;
+
+ public TraceCmdField(SWIGTYPE_p_tep_record record,
+ SWIGTYPE_p_tep_format_field field)
+ {
+ this.record = record;
+ this.field = field;
+ }
+
+ /**
+ * Get the field data as java byte array.
+ *
+ * @return the field data as byte array.
+ */
+ public Byte[] getData() throws TraceCmdException
+ {
+ byte ret[];
+ int rv, i;
+
+ if (this.data != null)
+ return this.data;
+
+ /* get the length at first */
+ rv = CTraceCmd.java_field_get_data(this.field, this.record,
+ new byte[0], 0);
+ if (rv < 0)
+ throw new TraceCmdException("Failed to get field data length, rv: " + rv);
+
+ ret = new byte[rv];
+ for (byte b:ret) {
+ b = 0;
+ }
+
+ rv = CTraceCmd.java_field_get_data(this.field, this.record,
+ ret, rv);
+ if (rv < 0)
+ throw new TraceCmdException("Failed to get field data, rv: " + rv);
+
+ Byte[] byteObjects = new Byte[ret.length];
+ i = 0;
+ for (byte b:ret)
+ byteObjects[i++] = b;
+
+ this.data = byteObjects;
+ return this.data;
+ }
+
+ /**
+ * Get the field data as BigInteger.
+ *
+ * @return the field data as BigInteger.
+ */
+ public BigInteger getNum() throws TraceCmdException
+ {
+ BigInteger[] val = { new BigInteger("0") };
+ SWIGTYPE_p_void rd;
+
+ rd = CTraceCmd.tep_record_data_get(this.record);
+ if (rd == null)
+ throw new TraceCmdException("Failed to retrieve field data");
+
+ int rv = CTraceCmd.tep_read_number_field(this.field, rd, val);
+ if (rv != 0)
+ throw new TraceCmdException("Failed to read field number, rv: " + rv);
+
+ return val[0];
+ }
+
+ @Override
+ public String toString()
+ {
+ String ret;
+
+ ret = CTraceCmd.java_field_get_str(this.field, this.record);
+ if (ret == null)
+ throw new TraceCmdException("Failed to retrieve field string");
+
+ return ret;
+ }
+}
diff --git a/java/ctracecmdjava.i b/java/ctracecmdjava.i
new file mode 100644
index 0000000..3bf6654
--- /dev/null
+++ b/java/ctracecmdjava.i
@@ -0,0 +1,180 @@
+// tracecmdjava.i
+%module CTraceCmd
+%include "typemaps.i"
+%include "constraints.i"
+%include "various.i"
+
+%apply Pointer NONNULL { struct tracecmd_input *handle };
+%apply Pointer NONNULL { struct tep_handle *pevent };
+%apply Pointer NONNULL { struct tep_format_field * };
+%apply unsigned long long *OUTPUT {unsigned long long *}
+%apply unsigned long *OUTPUT {unsigned long *}
+%apply char *BYTE { char *buf };
+%apply int *OUTPUT {int *}
+
+%{
+#include "trace-cmd.h"
+#include "event-parse.h"
+#include "event-utils.h"
+%}
+
+%inline %{
+struct tracecmd_input *tracecmd_alloc(const char *file, int flags);
+int tracecmd_read_headers(struct tracecmd_input *handle, int state);
+int tracecmd_cpus(struct tracecmd_input *handle);
+int tracecmd_long_size(struct tracecmd_input *handle);
+struct tep_record *
+tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu);
+struct tep_record *
+tracecmd_peek_data(struct tracecmd_input *handle, int cpu);
+void tracecmd_set_all_cpus_to_timestamp(struct tracecmd_input *handle,
+ unsigned long long time);
+
+unsigned long long tracecmd_record_ts_get(struct tep_record *record)
+{
+ return record->ts;
+}
+
+int tracecmd_record_cpu_get(struct tep_record *record)
+{
+ return record->cpu;
+}
+
+const char *tracecmd_event_name_get(const struct tep_event *event)
+{
+ return event->name;
+}
+
+const char *tracecmd_event_system_get(const struct tep_event *event)
+{
+ return event->system;
+}
+
+static inline struct tep_record *
+tracecmd_peek_data_ref(struct tracecmd_input *handle, int cpu)
+{
+ struct tep_record *rec = tracecmd_peek_data(handle, cpu);
+ if (rec)
+ rec->ref_count++;
+ return rec;
+}
+
+char *java_field_get_str(struct tep_format_field *f, struct tep_record *r)
+{
+ if (!strncmp(f->type, "__data_loc ", 11)) {
+ unsigned long long val;
+ int offset;
+
+ if (tep_read_number_field(f, r->data, &val))
+ return NULL;
+
+ /*
+ * The actual length of the dynamic array is stored
+ * in the top half of the field, and the offset
+ * is in the bottom half of the 32 bit field.
+ */
+ offset = val & 0xffff;
+ return r->data + offset;
+ }
+
+ return r->data + f->offset;
+}
+
+int java_field_get_data(struct tep_format_field *f, struct tep_record *r,
+ char *buf, size_t len)
+{
+ unsigned long long val;
+ int size, offset;
+
+ if (!strncmp(f->type, "__data_loc ", 11)) {
+ if (tep_read_number_field(f, r->data, &val))
+ return -1;
+
+ /*
+ * The actual length of the dynamic array is stored
+ * in the top half of the field, and the offset
+ * is in the bottom half of the 32 bit field.
+ */
+ offset = val & 0xffff;
+ size = val >> 16;
+ } else {
+ offset = f->offset;
+ size = f->size;
+ }
+
+ if (len < size)
+ return size;
+
+ memcpy(buf, r->data + offset, len);
+ return len;
+}
+
+static void *java_field_get_stack_init(struct tep_record *record,
+ struct tep_event *event)
+{
+ struct tep_format_field *field;
+ void *data = record->data;
+
+ field = tep_find_any_field(event, "caller");
+ if (!field)
+ return NULL;
+
+ return data + field->offset;
+}
+
+static int java_field_get_stack_check(struct tep_record *record,
+ struct tep_event *event,
+ void *data, int long_size,
+ unsigned long *addr)
+{
+ int rv;
+
+ rv = data < record->data + record->size;
+ if (!rv)
+ return 1;
+
+ *addr = tep_read_number(event->tep, data, long_size);
+ rv = ((long_size == 8 && *addr == (unsigned long long)-1) ||
+ ((int)*addr == -1));
+
+ return rv;
+}
+
+static void *java_field_get_stack_next(void *data, int long_size)
+{
+ return data + long_size;
+}
+
+static const char *java_field_get_stack_str(struct tep_event *event,
+ unsigned long addr)
+{
+ return tep_find_function(event->tep, addr);
+}
+
+struct tep_format_field *tracecmd_event_format_fields_get(struct tep_event *event)
+{
+ return event->format.fields;
+}
+
+struct tep_format_field *tracecmd_field_next_get(struct tep_format_field *f)
+{
+ return f->next;
+}
+
+char *tracecmd_field_name_get(struct tep_format_field *f)
+{
+ return f->name;
+}
+%}
+
+%ignore trace_seq_vprintf;
+%ignore vpr_stat;
+
+/* SWIG can't grok these, define them to nothing */
+#define __trace
+#define __attribute__(x)
+#define __thread
+
+%include "trace-cmd.h"
+%include <trace-seq.h>
+%include <event-parse.h>
diff --git a/java/example/Makefile b/java/example/Makefile
new file mode 100644
index 0000000..f6308cb
--- /dev/null
+++ b/java/example/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+all:
+ $(JAVA_HOME)/bin/javac -classpath ../tracecmd.jar TraceCmdExample.java
+
+clean:
+ $(RM) *.class
diff --git a/java/example/TraceCmdExample.java b/java/example/TraceCmdExample.java
new file mode 100644
index 0000000..a6fef87
--- /dev/null
+++ b/java/example/TraceCmdExample.java
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Red Hat Inc, Alexander Aring <aahringo@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+import tracecmd.*;
+
+/**
+* Very small example how to use libtracecmd java bindings.
+*
+* Run as:
+*
+* $JAVA_HOME/bin/java -cp ../tracecmd.jar:. TraceCmdExample $TRACE.DAT
+*
+* @author Alexander Aring <aahringo@redhat.com>
+*
+*/
+public class TraceCmdExample
+{
+ public static final void main(String[] args)
+ {
+ TraceCmd t = new TraceCmd(args[0]);
+
+ System.out.println("First Timestamp: " + t.getFirstTs());
+ System.out.println("Amount of Cpus: " + t.getCpus());
+
+ for (TraceCmdEvent e:t) {
+ System.out.println(e);
+ }
+ }
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH/RFC] java: add initial bindings
2022-06-20 20:45 [PATCH/RFC] java: add initial bindings Alexander Aring
@ 2022-07-08 18:30 ` Steven Rostedt
2022-07-13 13:40 ` Alexander Aring
0 siblings, 1 reply; 3+ messages in thread
From: Steven Rostedt @ 2022-07-08 18:30 UTC (permalink / raw)
To: Alexander Aring; +Cc: linux-trace-devel, cluster-devel
On Mon, 20 Jun 2022 16:45:39 -0400
Alexander Aring <aahringo@redhat.com> wrote:
Hi Alexander!
> This patch adds basic java bindings for libtracecmd. It currently
> supports very basic handling of parsing trace-cmd recorded trace files.
> There might of course additional improvements regarding swig bindings and
> the memory handling. E.g. in javas builtin iterator for-each handling
> everything will keep in memory. The license for the high-level java
> bindings jar file is LGPL-2.1 and is the same as libtracecmd.
>
> The author of this patch created a java application that uses those java
> bindings to trace locks and represent them in graphical GANTT diagram,
> see [0].
>
> You need to set the JAVA_HOME environment variable to let the Makefile know
> that it builds the java bindings. This is somehow standard in the java world
> as replacement for pkg-config or similar. There should no trace-cmd java
> dependency, the recommended way should be to provide a kind of trace-cmd-java
> package from your distribution containing the tracecmd.jar and
> libctracecmdjava.so. This package would have then a java dependency to
> e.g. OpenJDK, that I was using to test those bindings for.
>
> The author is not a swig expert but it works as it currently is. Also
> the author did not hit issues because memory _yet_. Those are beginning
> experimental bindings and can be changed/improved in future.
Are you "the author"?
Could you rephrase this better?
Like:
"There may be better ways to do this with swig, but this appears to work as
is, as there were no issues because of memory _yet_."
That is, please try to rewrite the change log without referring to yourself.
>
> [0] https://gitlab.com/netcoder/dlm2slog2
> Signed-off-by: Alexander Aring <aahringo@redhat.com>
> ---
> .gitignore | 4 +
> Makefile | 60 ++++++-
> java/Makefile | 39 +++++
> java/TraceCmd.java | 236 +++++++++++++++++++++++++
> java/TraceCmdEvent.java | 277 ++++++++++++++++++++++++++++++
> java/TraceCmdException.java | 16 ++
> java/TraceCmdField.java | 104 +++++++++++
> java/ctracecmdjava.i | 180 +++++++++++++++++++
> java/example/Makefile | 7 +
> java/example/TraceCmdExample.java | 33 ++++
> 10 files changed, 953 insertions(+), 3 deletions(-)
> create mode 100644 java/Makefile
> create mode 100644 java/TraceCmd.java
> create mode 100644 java/TraceCmdEvent.java
> create mode 100644 java/TraceCmdException.java
> create mode 100644 java/TraceCmdField.java
> create mode 100644 java/ctracecmdjava.i
> create mode 100644 java/example/Makefile
> create mode 100644 java/example/TraceCmdExample.java
>
I'm not a swig nor Java expert, and will likely not even test this code.
But I'm happy to include it if it doesn't break anything that I do test.
But if anyone complains of breakage, I'll simply point them to you ;-)
Are you OK with that?
Hmm, I should add a MAINTAINERS file to add people that support different
portions of trace-cmd.
-- Steve
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH/RFC] java: add initial bindings
2022-07-08 18:30 ` Steven Rostedt
@ 2022-07-13 13:40 ` Alexander Aring
0 siblings, 0 replies; 3+ messages in thread
From: Alexander Aring @ 2022-07-13 13:40 UTC (permalink / raw)
To: Steven Rostedt; +Cc: linux-trace-devel, cluster-devel
Hi,
On Fri, Jul 8, 2022 at 2:31 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
> On Mon, 20 Jun 2022 16:45:39 -0400
> Alexander Aring <aahringo@redhat.com> wrote:
>
> Hi Alexander!
>
>
> > This patch adds basic java bindings for libtracecmd. It currently
> > supports very basic handling of parsing trace-cmd recorded trace files.
> > There might of course additional improvements regarding swig bindings and
> > the memory handling. E.g. in javas builtin iterator for-each handling
> > everything will keep in memory. The license for the high-level java
> > bindings jar file is LGPL-2.1 and is the same as libtracecmd.
> >
> > The author of this patch created a java application that uses those java
> > bindings to trace locks and represent them in graphical GANTT diagram,
> > see [0].
> >
> > You need to set the JAVA_HOME environment variable to let the Makefile know
> > that it builds the java bindings. This is somehow standard in the java world
> > as replacement for pkg-config or similar. There should no trace-cmd java
> > dependency, the recommended way should be to provide a kind of trace-cmd-java
> > package from your distribution containing the tracecmd.jar and
> > libctracecmdjava.so. This package would have then a java dependency to
> > e.g. OpenJDK, that I was using to test those bindings for.
> >
> > The author is not a swig expert but it works as it currently is. Also
> > the author did not hit issues because memory _yet_. Those are beginning
> > experimental bindings and can be changed/improved in future.
>
>
> Are you "the author"?
>
yes.
> Could you rephrase this better?
>
> Like:
>
> "There may be better ways to do this with swig, but this appears to work as
> is, as there were no issues because of memory _yet_."
>
> That is, please try to rewrite the change log without referring to yourself.
>
okay.
> >
> > [0] https://gitlab.com/netcoder/dlm2slog2
> > Signed-off-by: Alexander Aring <aahringo@redhat.com>
> > ---
> > .gitignore | 4 +
> > Makefile | 60 ++++++-
> > java/Makefile | 39 +++++
> > java/TraceCmd.java | 236 +++++++++++++++++++++++++
> > java/TraceCmdEvent.java | 277 ++++++++++++++++++++++++++++++
> > java/TraceCmdException.java | 16 ++
> > java/TraceCmdField.java | 104 +++++++++++
> > java/ctracecmdjava.i | 180 +++++++++++++++++++
> > java/example/Makefile | 7 +
> > java/example/TraceCmdExample.java | 33 ++++
> > 10 files changed, 953 insertions(+), 3 deletions(-)
> > create mode 100644 java/Makefile
> > create mode 100644 java/TraceCmd.java
> > create mode 100644 java/TraceCmdEvent.java
> > create mode 100644 java/TraceCmdException.java
> > create mode 100644 java/TraceCmdField.java
> > create mode 100644 java/ctracecmdjava.i
> > create mode 100644 java/example/Makefile
> > create mode 100644 java/example/TraceCmdExample.java
> >
>
> I'm not a swig nor Java expert, and will likely not even test this code.
> But I'm happy to include it if it doesn't break anything that I do test.
> But if anyone complains of breakage, I'll simply point them to you ;-)
>
> Are you OK with that?
>
> Hmm, I should add a MAINTAINERS file to add people that support different
> portions of trace-cmd.
>
I already created a new project trace-cmd-java [0]. This will static
link against libtracecmd and their dependencies. At the end it
produces a binary blob for the java bindings. The advantage here is
that I have full control over the used version of libtracecmd and
their dependencies.
I would like to go this way and see how it goes. Meanwhile I can do
more testing and cleanups. I may come back again to bring it upstream.
- Alex
[0] https://gitlab.com/netcoder/trace-cmd-java
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-07-13 13:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-06-20 20:45 [PATCH/RFC] java: add initial bindings Alexander Aring
2022-07-08 18:30 ` Steven Rostedt
2022-07-13 13:40 ` Alexander Aring
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).