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@xxxxxxxxxx> --- .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@xxxxxxxxxx> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +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@xxxxxxxxxx> +* +*/ +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@xxxxxxxxxx> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +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@xxxxxxxxxx> +* +*/ +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@xxxxxxxxxx> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +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@xxxxxxxxxx> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +package tracecmd; + +import java.math.BigInteger; + +import tracecmd.swig.*; + +/** +* TraceCmdEventField represent a field of an trace event. +* +* @author Alexander Aring <aahringo@xxxxxxxxxx> +* +*/ +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@xxxxxxxxxx> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +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@xxxxxxxxxx> +* +*/ +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
![]() |