linux-trace-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session
@ 2019-12-03 10:35 Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 01/18] trace-cmd: Implement new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov (VMware)
                   ` (17 more replies)
  0 siblings, 18 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Basic infrastructure for host - guest timestamp synchronization and a
POC implementation of PTP-like algorithm. 

[
 v17 changes:
  - Implemented new PTP logic for calculating the clocks offset, using
    histogram of all PTP samples. It gives better results than the logic
    with the fastest response time, so set the new one as default in the
    POC patch.

 v16 changes:
  - Fixed compilation in case no VSOCK is available (Thanks to Slavomir Kaslev)
  - Fixed a typo in trace-cmd-record.1.txt (Thanks to Slavomir Kaslev)
  - Added forgotten file in the patch "trace-cmd: Add new library APIs for
    ftrace instances." - trace-instance.c
  - Fixed few compilation warnings related to TSYNC_DEBUG code.
  - Removed a blank line at the end of "tsync_readme" file. 

 v15 changes:
  - Removed the patch for "--proc-map" from the series, as it should not
    be part of it.

 v14 changes:
  - Bring back the PTP-like algorithm and removed the ftrace event based logic.
  - Reimplemented the PTP-like algorithm to use raw ftrace markers, instead of clock_gettime() API.
  - Refactored the logic to be algorithm independent and plugin friendly.
  - Implemented continuous timestamps synchronization, while the trace is running.
  - Moved logic from trace-cmd application to libtracecmd, as new library APIs.
  - Implemented new trace id functionality.
  - Implemented new guest section in host trace.dat file.
	
 v13 changes:
  - Remove few patches from the set, as they were merged.
  - Rebased to the latest master, Slavomirs patchest "Add VM kernel tracing over
    vsockets and FIFOs" got merged!
 	
 v12 changes:
  - Rebased on top of Slavomir's v13 "Add VM kernel tracing over vsockets and FIFOs"

 v11 changes:
  - Rebased on top of Slavomir's v10 "Add VM kernel tracing over vsockets and FIFOs"
  - Addressed Slavomir's commnents from version 10 of the patch series.

 v10 changes:
  - Fixed broken compilation, call to timestamp_correction_calc() in timestamp_correct
    was smashed.
  - Replaced deprecated tep_data_event_from_type() API with tep_find_event().
  - Fixed a warning on assignment const to non const.

 v9 changes:
  - Fixed implementation of binary search algorithm in timestamp_correct()

 v8 changes:
  - Added rmdir() call in tracecmd_remove_instance(), to completely remove the instance. 
  However, there is an issue with deleting the instances using rmdir(), which is investigated.
  - Few changes in read_qemu_guests_pids(), timestamp_correct(), tsync_offset_load() 
 tracecmd_clock_context_new() and find_raw_events() suggested by Slavomir. 

 v7 changes:
  - Added warning messages in case time synchronization cannot be negotiated or fails.
  - Few optimizations and checks in read_qemu_guests_pids(), tsync_offset_load(),
    and find_raw_events(), suggested by Slavomir Kaslev.
  - Reworked timestamp_correct() to not use static variables.
  - Check TRACECMD_OPTION_TIME_SHIFT before reading time sync samples from the trace.dat file

 v6 changes:
  - Refactored tracecmd_msg_snd_time_sync() and tracecmd_msg_rcv_time_sync() functions:
    removed any time sync calculations logic as separate functions in trace-timesync.c file
  - Defined TSYNC_PROBE, TSYNC_REQ and TSYNC_RESP messages, in order to make the time sync
    protocol comprehensible.
  - Addressed Steven Rostedt comments.
  - Addressed Slavomir Kaslev commnets.

v5 changes:
  - Rebased to Slavomir's v8 "Add VM kernel tracing over vsockets and FIFOs"
    patch series.
  - Implemented an algorithm for time drift correction.
  - Addressed Slavomir's commnets.
  - Refactored the code: moved all time sync specific implementation in trace-timesync.c
  - Isolated all hardcoded event specific stuff in a structure, so it could be easily
    moved to external plugins.
  - Added a check for VSOCK support: do not perform vsock dependent time synchronisation
    in case there is no VSOCK support.

 v4 changes:
  - Removed the implementation of PTP-like algorithm. The current
    logic relies on matching time stamps of kvm_exit/virtio_transport_recv_pkt
    events on host to virtio_transport_alloc_pkt/vp_notify events on guest.
  - Rebased to Slavomir's v7 "Add VM kernel tracing over vsockets and FIFOs"
    patch series.
  - Decreased the time synch probes from 5000 to 300.
  - Addressed Steven Rostedt comments.
  - Code cleanup.

 v3 changes:
 - Removed any magic constants, used in the PTP-like algorithm,
   as Slavomir Kaslev suggested.
 - Implemented new algorithm, based on mapping kvm_exit events
   in host context to vsock_send events in guest context,
   suggested by Steven Rostedt.

 v2 changes:
  - Addressed Steven Rostedt comments.
  - Modified PTP-like timestamps sync algorithm to gain more accuracy, with the
    help of Yordan Karadzhov and Slavomir Kaslev.
]

Tzvetomir Stoyanov (7):
  trace-cmd: Implement new lib API: tracecmd_local_events_system()
  trace-cmd: Add support for negative time offsets in trace.dat file
  trace-cmd: Add implementations of htonll() and ntohll()
  trace-cmd: Add new library APIs for ftrace instances.
  trace-cmd: Find and store pids of tasks, which run virtual CPUs of
    given VM
  trace-cmd: Implement new API tracecmd_add_option_v()
  trace-cmd: Implement new option in trace.dat file:
    TRACECMD_OPTION_TIME_SHIFT

Tzvetomir Stoyanov (VMware) (11):
  trace-cmd: Add new library API for local CPU count
  trace-cmd: Add new library API for reading ftrace buffers
  trace-cmd: Add new API to generate a unique ID of the tracing session
  trace-cmd: Store the session tracing ID in the trace.dat file
  trace-cmd: Exchange tracing IDs between host and guest
  trace-cmd: Add guest information in host's trace.dat file
  trace-cmd: Add host trace clock as guest trace argument
  trace-cmd: Refactor few trace-cmd internal functions.
  trace-cmd: Basic infrastructure for host - guest timestamp
    synchronization
  trace-cmd: [POC] PTP-like algorithm for host - guest timestamp
    synchronization
  trace-cmd: Debug scripts for PTP-like algorithm for host - guest
    timestamp synchronization

 Documentation/trace-cmd-record.1.txt      |   6 +
 include/trace-cmd/trace-cmd.h             | 116 +++-
 include/trace-cmd/trace-msg.h             |  10 +
 include/traceevent/event-parse.h          |   1 +
 lib/trace-cmd/Makefile                    |   3 +
 lib/trace-cmd/include/trace-cmd-local.h   |  33 +-
 lib/trace-cmd/include/trace-tsync-local.h |  36 ++
 lib/trace-cmd/trace-input.c               | 314 ++++++++++-
 lib/trace-cmd/trace-instance.c            | 265 +++++++++
 lib/trace-cmd/trace-msg.c                 | 162 +++++-
 lib/trace-cmd/trace-output.c              | 116 +++-
 lib/trace-cmd/trace-timesync-ptp.c        | 649 ++++++++++++++++++++++
 lib/trace-cmd/trace-timesync.c            | 469 ++++++++++++++++
 lib/trace-cmd/trace-util.c                | 358 ++++++++++--
 scripts/debug/tsync_hist.py               |  57 ++
 scripts/debug/tsync_readme                |  12 +
 scripts/debug/tsync_res.py                |  46 ++
 tracecmd/Makefile                         |   3 +-
 tracecmd/include/trace-local.h            |  33 +-
 tracecmd/trace-agent.c                    |  48 +-
 tracecmd/trace-list.c                     |   2 +-
 tracecmd/trace-profile.c                  |   2 +-
 tracecmd/trace-read.c                     |   4 +-
 tracecmd/trace-record.c                   | 600 ++++++++++----------
 tracecmd/trace-show.c                     |   2 +
 tracecmd/trace-stat.c                     |  24 +-
 tracecmd/trace-tsync.c                    | 268 +++++++++
 tracecmd/trace-usage.c                    |   4 +
 28 files changed, 3199 insertions(+), 444 deletions(-)
 create mode 100644 lib/trace-cmd/include/trace-tsync-local.h
 create mode 100644 lib/trace-cmd/trace-instance.c
 create mode 100644 lib/trace-cmd/trace-timesync-ptp.c
 create mode 100644 lib/trace-cmd/trace-timesync.c
 create mode 100644 scripts/debug/tsync_hist.py
 create mode 100644 scripts/debug/tsync_readme
 create mode 100644 scripts/debug/tsync_res.py
 create mode 100644 tracecmd/trace-tsync.c

-- 
2.23.0


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v17 01/18] trace-cmd: Implement new lib API: tracecmd_local_events_system()
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 02/18] trace-cmd: Add support for negative time offsets in trace.dat file Tzvetomir Stoyanov (VMware)
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

The new tracecmd lib API tracecmd_local_events_system() creates
a tep handler and initializes it with the events of the
specified subsystems.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 include/trace-cmd/trace-cmd.h |   4 +-
 lib/trace-cmd/trace-util.c    | 102 +++++++++++++++++++++++-----------
 2 files changed, 72 insertions(+), 34 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 13afce7..9363a26 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -27,8 +27,10 @@ extern int tracecmd_disable_plugins;
 char **tracecmd_event_systems(const char *tracing_dir);
 char **tracecmd_system_events(const char *tracing_dir, const char *system);
 struct tep_handle *tracecmd_local_events(const char *tracing_dir);
+struct tep_handle *tracecmd_local_events_system(const char *tracing_dir,
+						const char * const *sys_names);
 int tracecmd_fill_local_events(const char *tracing_dir,
-			       struct tep_handle *pevent, int *parsing_failures);
+			       struct tep_handle *tep, int *parsing_failures);
 char **tracecmd_local_plugins(const char *tracing_dir);
 
 char **tracecmd_add_list(char **list, const char *name, int len);
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index b5bb0d5..49a80ae 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -616,39 +616,20 @@ static int read_header(struct tep_handle *pevent, const char *events_dir)
 	return ret;
 }
 
-/**
- * tracecmd_local_events - create a pevent from the events on system
- * @tracing_dir: The directory that contains the events.
- *
- * Returns a pevent structure that contains the pevents local to
- * the system.
- */
-struct tep_handle *tracecmd_local_events(const char *tracing_dir)
+static bool contains(const char *name, const char * const *names)
 {
-	struct tep_handle *pevent = NULL;
-
-	pevent = tep_alloc();
-	if (!pevent)
-		return NULL;
-
-	if (tracecmd_fill_local_events(tracing_dir, pevent, NULL)) {
-		tep_free(pevent);
-		pevent = NULL;
-	}
-
-	return pevent;
+	if (!names)
+		return false;
+	for (; *names; names++)
+		if (strcmp(name, *names) == 0)
+			return true;
+	return false;
 }
 
-/**
- * tracecmd_fill_local_events - Fill a pevent with the events on system
- * @tracing_dir: The directory that contains the events.
- * @pevent: Allocated pevent which will be filled
- * @parsing_failures: return number of failures while parsing the event files
- *
- * Returns whether the operation succeeded
- */
-int tracecmd_fill_local_events(const char *tracing_dir,
-			       struct tep_handle *pevent, int *parsing_failures)
+static int tracecmd_fill_local_events_system(const char *tracing_dir,
+					     struct tep_handle *tep,
+					     const char * const *sys_names,
+					     int *parsing_failures)
 {
 	struct dirent *dent;
 	char *events_dir;
@@ -677,7 +658,7 @@ int tracecmd_fill_local_events(const char *tracing_dir,
 		goto out_free;
 	}
 
-	ret = read_header(pevent, events_dir);
+	ret = read_header(tep, events_dir);
 	if (ret < 0) {
 		ret = -1;
 		goto out_free;
@@ -690,7 +671,8 @@ int tracecmd_fill_local_events(const char *tracing_dir,
 		if (strcmp(name, ".") == 0 ||
 		    strcmp(name, "..") == 0)
 			continue;
-
+		if (sys_names && !contains(name, sys_names))
+			continue;
 		sys = append_file(events_dir, name);
 		ret = stat(sys, &st);
 		if (ret < 0 || !S_ISDIR(st.st_mode)) {
@@ -698,7 +680,7 @@ int tracecmd_fill_local_events(const char *tracing_dir,
 			continue;
 		}
 
-		ret = load_events(pevent, name, sys);
+		ret = load_events(tep, name, sys);
 
 		free(sys);
 
@@ -716,6 +698,60 @@ int tracecmd_fill_local_events(const char *tracing_dir,
 	return ret;
 }
 
+/**
+ * tracecmd_local_events_system - create a tep from the events of the specified subsystem.
+ *
+ * @tracing_dir: The directory that contains the events.
+ * @sys_name: Array of system names, to load the events from.
+ * The last element from the array must be NULL
+ *
+ * Returns a tep structure that contains the tep local to
+ * the system.
+ */
+struct tep_handle *tracecmd_local_events_system(const char *tracing_dir,
+						const char * const *sys_names)
+{
+	struct tep_handle *tep = NULL;
+
+	tep = tep_alloc();
+	if (!tep)
+		return NULL;
+
+	if (tracecmd_fill_local_events_system(tracing_dir, tep, sys_names, NULL)) {
+		tep_free(tep);
+		tep = NULL;
+	}
+
+	return tep;
+}
+
+/**
+ * tracecmd_local_events - create a pevent from the events on system
+ * @tracing_dir: The directory that contains the events.
+ *
+ * Returns a pevent structure that contains the pevents local to
+ * the system.
+ */
+struct tep_handle *tracecmd_local_events(const char *tracing_dir)
+{
+	return tracecmd_local_events_system(tracing_dir, NULL);
+}
+
+/**
+ * tracecmd_fill_local_events - Fill a pevent with the events on system
+ * @tracing_dir: The directory that contains the events.
+ * @tep: Allocated tep handler which will be filled
+ * @parsing_failures: return number of failures while parsing the event files
+ *
+ * Returns whether the operation succeeded
+ */
+int tracecmd_fill_local_events(const char *tracing_dir,
+			       struct tep_handle *tep, int *parsing_failures)
+{
+	return tracecmd_fill_local_events_system(tracing_dir, tep,
+						 NULL, parsing_failures);
+}
+
 /**
  * tracecmd_local_plugins - returns an array of available tracer plugins
  * @tracing_dir: The directory that contains the tracing directory
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 02/18] trace-cmd: Add support for negative time offsets in trace.dat file
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 01/18] trace-cmd: Implement new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll() Tzvetomir Stoyanov (VMware)
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

When synchronizing timestamps between different machines, there are cases
when the time offset is negative. This patch changes the way time offset is
written and read from trace.dat file - as signed decimal, instead of hex.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 include/trace-cmd/trace-cmd.h | 2 +-
 lib/trace-cmd/trace-input.c   | 6 +++---
 tracecmd/trace-read.c         | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 9363a26..7f9cb73 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -138,7 +138,7 @@ const char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int ind
 struct tracecmd_input *tracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx);
 int tracecmd_is_buffer_instance(struct tracecmd_input *handle);
 
-void tracecmd_set_ts_offset(struct tracecmd_input *handle, unsigned long long offset);
+void tracecmd_set_ts_offset(struct tracecmd_input *handle, long long offset);
 void tracecmd_set_ts2secs(struct tracecmd_input *handle, unsigned long long hz);
 
 void tracecmd_print_events(struct tracecmd_input *handle, const char *regex);
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index 3b187e3..91362f3 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -90,7 +90,7 @@ struct tracecmd_input {
 	bool			read_page;
 	bool			use_pipe;
 	struct cpu_data 	*cpu_data;
-	unsigned long long	ts_offset;
+	long long		ts_offset;
 	double			ts2secs;
 	char *			cpustats;
 	char *			uname;
@@ -2127,7 +2127,7 @@ static int init_cpu(struct tracecmd_input *handle, int cpu)
 }
 
 void tracecmd_set_ts_offset(struct tracecmd_input *handle,
-			    unsigned long long offset)
+			    long long offset)
 {
 	handle->ts_offset = offset;
 }
@@ -2305,7 +2305,7 @@ tracecmd_search_task_map(struct tracecmd_input *handle,
 
 static int handle_options(struct tracecmd_input *handle)
 {
-	unsigned long long offset;
+	long long offset;
 	unsigned short option;
 	unsigned int size;
 	char *cpustats = NULL;
diff --git a/tracecmd/trace-read.c b/tracecmd/trace-read.c
index c0d640d..4ce2e98 100644
--- a/tracecmd/trace-read.c
+++ b/tracecmd/trace-read.c
@@ -58,7 +58,7 @@ static struct list_head handle_list;
 struct input_files {
 	struct list_head	list;
 	const char		*file;
-	unsigned long long	tsoffset;
+	long long		tsoffset;
 	unsigned long long	ts2secs;
 };
 static struct list_head input_files;
@@ -1466,7 +1466,7 @@ void trace_report (int argc, char **argv)
 	struct input_files *inputs;
 	struct handle_list *handles;
 	enum output_type otype;
-	unsigned long long tsoffset = 0;
+	long long tsoffset = 0;
 	unsigned long long ts2secs = 0;
 	unsigned long long ts2sc;
 	int show_stat = 0;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll()
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 01/18] trace-cmd: Implement new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 02/18] trace-cmd: Add support for negative time offsets in trace.dat file Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-20 13:50   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances Tzvetomir Stoyanov (VMware)
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

Implementations of htonll() and ntohll() are added as
macros, if they are not already defined.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 include/trace-cmd/trace-msg.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/include/trace-cmd/trace-msg.h b/include/trace-cmd/trace-msg.h
index aab8a69..654ca43 100644
--- a/include/trace-cmd/trace-msg.h
+++ b/include/trace-cmd/trace-msg.h
@@ -12,4 +12,14 @@
 
 extern unsigned int page_size;
 
+#ifndef htonll
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htonll(x) __bswap_64(x)
+#define ntohll(x) __bswap_64(x)
+#else
+#define htonll(x) (x)
+#define ntohll(x) (x)
+#endif
+#endif
+
 #endif /* _TRACE_MSG_H_ */
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances.
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (2 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll() Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-04 16:17   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 05/18] trace-cmd: Add new library API for local CPU count Tzvetomir Stoyanov (VMware)
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

In order to reuse the code, the functionality related to
ftrace instances is moved from trace-cmd application to
libtracecmd. The following new library APIs are introduced:

library structure, representing a ftrace instance:
	struct tracecmd_instance {
        	char    *name;
	        char    *clock;
	};

APIs for creating and deleting ftrace instances:
	struct tracecmd_instance *tracecmd_create_instance(const char *name);
	void tracecmd_free_instance(struct tracecmd_instance *instance);
	int tracecmd_make_instance(struct tracecmd_instance *instance);
	void tracecmd_remove_instance(struct tracecmd_instance *instance);

APIs for reading and writing ftrace files, instance aware:
	char *tracecmd_get_instance_file(struct tracecmd_instance *instance, const char *file);
	char *tracecmd_get_instance_dir(struct tracecmd_instance *instance);
	int tracecmd_write_instance_file(struct tracecmd_instance *instance,
	                                 const char *file, const char *str,
        	                         const char *type);
	int tracecmd_write_file(const char *file, const char *str, const char *type);
	char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
        	                          char *file, int *psize);

API for setting ftrace clock, instance aware:
	void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock);

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h           |  24 ++
 lib/trace-cmd/Makefile                  |   1 +
 lib/trace-cmd/include/trace-cmd-local.h |  33 +--
 lib/trace-cmd/trace-instance.c          | 265 ++++++++++++++++++
 lib/trace-cmd/trace-util.c              |  71 ++++-
 tracecmd/include/trace-local.h          |   6 +-
 tracecmd/trace-list.c                   |   2 +-
 tracecmd/trace-record.c                 | 341 +++++++-----------------
 tracecmd/trace-show.c                   |   2 +
 tracecmd/trace-stat.c                   |  20 +-
 10 files changed, 472 insertions(+), 293 deletions(-)
 create mode 100644 lib/trace-cmd/trace-instance.c

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 7f9cb73..5287d23 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -356,6 +356,30 @@ int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int *nr_cpus, int *page_size,
 				 unsigned int **ports, bool *use_fifos);
+/* --- ftrace instances --- */
+
+struct tracecmd_instance {
+	char	*name;
+	char	*clock;
+};
+
+struct tracecmd_instance *tracecmd_create_instance(const char *name);
+void tracecmd_free_instance(struct tracecmd_instance *instance);
+int tracecmd_make_instance(struct tracecmd_instance *instance);
+void tracecmd_remove_instance(struct tracecmd_instance *instance);
+char *
+tracecmd_get_instance_file(struct tracecmd_instance *instance, const char *file);
+char *tracecmd_get_instance_dir(struct tracecmd_instance *instance);
+int tracecmd_write_instance_file(struct tracecmd_instance *instance,
+				 const char *file, const char *str,
+				 const char *type);
+
+int tracecmd_write_file(const char *file, const char *str, const char *type);
+char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
+				  char *file, int *psize);
+
+void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock);
+
 
 /* --- Plugin handling --- */
 extern struct tep_plugin_option trace_ftrace_options[];
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 3b4b5aa..18c7013 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -13,6 +13,7 @@ OBJS += trace-input.o
 OBJS += trace-output.o
 OBJS += trace-recorder.o
 OBJS += trace-util.o
+OBJS += trace-instance.o
 OBJS += trace-filter-hash.o
 OBJS += trace-msg.o
 
diff --git a/lib/trace-cmd/include/trace-cmd-local.h b/lib/trace-cmd/include/trace-cmd-local.h
index 09574db..eef4d39 100644
--- a/lib/trace-cmd/include/trace-cmd-local.h
+++ b/lib/trace-cmd/include/trace-cmd-local.h
@@ -18,36 +18,7 @@
 #define STR(x)	_STR(x)
 #define FILE_VERSION_STRING STR(FILE_VERSION)
 
-static ssize_t __do_write(int fd, const void *data, size_t size)
-{
-	ssize_t tot = 0;
-	ssize_t w;
-
-	do {
-		w = write(fd, data + tot, size - tot);
-		tot += w;
-
-		if (!w)
-			break;
-		if (w < 0)
-			return w;
-	} while (tot != size);
-
-	return tot;
-}
-
-static ssize_t
-__do_write_check(int fd, const void *data, size_t size)
-{
-	ssize_t ret;
-
-	ret = __do_write(fd, data, size);
-	if (ret < 0)
-		return ret;
-	if (ret != size)
-		return -1;
-
-	return 0;
-}
+ssize_t __do_write_check(int fd, const void *data, size_t size);
+void __noreturn die(const char *fmt, ...); /* Can be overriden */
 
 #endif /* _TRACE_CMD_LOCAL_H */
diff --git a/lib/trace-cmd/trace-instance.c b/lib/trace-cmd/trace-instance.c
new file mode 100644
index 0000000..1175b8c
--- /dev/null
+++ b/lib/trace-cmd/trace-instance.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <time.h>
+#include <poll.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "trace-cmd.h"
+#include "trace-cmd-local.h"
+
+/**
+ * tracecmd_put_tracing_file - Free tracing file / dir, created by
+ *		tracecmd_get_instance_dir() or tracecmd_get_instance_file()
+ *		APIs.
+ *@name: The name of the tracing file or dir
+ */
+void tracecmd_put_tracing_file(char *name)
+{
+	free(name);
+}
+
+/**
+ * tracecmd_create_instance - allocate a new ftrace instance
+ * @name: The name of the instance (instance will point to this)
+ *
+ * Returns a newly allocated instance. Note that @name will not be
+ * copied, and the instance buffer will point to the string itself.
+ */
+struct tracecmd_instance *tracecmd_create_instance(const char *name)
+{
+	struct tracecmd_instance *instance;
+
+	instance = malloc(sizeof(*instance));
+	if (!instance)
+		return NULL;
+	memset(instance, 0, sizeof(*instance));
+	if (name)
+		instance->name = strdup(name);
+
+	return instance;
+}
+
+/**
+ * tracecmd_free_instance - Free an instance struct, previously allocated by
+ *			    tracecmd_create_instance().
+ *@instance: Pointer to the instance to be freed
+ *
+ */
+void tracecmd_free_instance(struct tracecmd_instance *instance)
+{
+	if (!instance)
+		return;
+
+	free(instance->name);
+	free(instance);
+}
+
+/**
+ * tracecmd_make_instance - Create a new ftrace instance
+ * @instance: Pointer to the instance to be created
+ *
+ * Returns -1 in case of an erro, or 0 otherwise.
+ */
+int tracecmd_make_instance(struct tracecmd_instance *instance)
+{
+	struct stat st;
+	char *path;
+	int ret;
+
+	path = tracecmd_get_instance_dir(instance);
+	ret = stat(path, &st);
+	if (ret < 0) {
+		ret = mkdir(path, 0777);
+		if (ret < 0)
+			return ret;
+
+	} else
+		ret = 1;
+	tracecmd_put_tracing_file(path);
+	return ret;
+}
+
+/**
+ * tracecmd_remove_instance - Remove a ftrace instance
+ * @instance: Pointer to the instance to be removed
+ *
+ */
+void tracecmd_remove_instance(struct tracecmd_instance *instance)
+{
+	char *path;
+
+	path = tracecmd_get_instance_dir(instance);
+	rmdir(path);
+	tracecmd_put_tracing_file(path);
+}
+
+/**
+ * tracecmd_get_instance_file - return the path to a instance file.
+ * @instance: buffer instance for the file, can be NULL for the top instance
+ * @file: name of file to return
+ *
+ * Returns the path name of the @file for the given @instance.
+ *
+ * Must use tracecmd_put_tracing_file() to free the returned string.
+ */
+char *
+tracecmd_get_instance_file(struct tracecmd_instance *instance, const char *file)
+{
+	char *path;
+	char *buf;
+	int ret;
+
+	if (instance && instance->name) {
+		ret = asprintf(&buf, "instances/%s/%s", instance->name, file);
+		if (ret < 0)
+			die("Failed to allocate name for %s/%s", instance->name, file);
+		path = tracecmd_get_tracing_file(buf);
+		free(buf);
+	} else
+		path = tracecmd_get_tracing_file(file);
+
+	return path;
+}
+
+/**
+ * tracecmd_get_instance_file - return the path to a instance file.
+ * @instance: buffer instance for the file, can be NULL for the top instance
+ * @file: name of file to return
+ *
+ * Returns the path name of the @file for the given @instance.
+ *
+ * Must use tracecmd_put_tracing_file() to free the returned string.
+ */
+char *tracecmd_get_instance_dir(struct tracecmd_instance *instance)
+{
+	char *buf;
+	char *path;
+	int ret;
+
+	if (instance->name) {
+		ret = asprintf(&buf, "instances/%s", instance->name);
+		if (ret < 0)
+			die("Failed to allocate for instance %s", instance->name);
+		path = tracecmd_get_tracing_file(buf);
+		free(buf);
+	} else
+		path = tracecmd_find_tracing_dir();
+
+	return path;
+}
+
+/**
+ * tracecmd_write_instance_file - Write in trace file of specific instance.
+ * @instance: buffer instance for the file, can be NULL for the top instance
+ * @file: name of the file
+ * @str: Null terminated string, that will be written in the file.
+ * @type: Null terminated string, describing the current write operation.
+ *	  Used for logging purposes.
+ *
+ * Returns the number of written bytes, or -1 in case of an error
+ */
+int tracecmd_write_instance_file(struct tracecmd_instance *instance,
+				 const char *file, const char *str,
+				 const char *type)
+{
+	struct stat st;
+	char *path;
+	int ret;
+
+	path = tracecmd_get_instance_file(instance, file);
+	ret = stat(path, &st);
+	if (ret == 0)
+		ret = tracecmd_write_file(path, str, type);
+	tracecmd_put_tracing_file(path);
+
+	return ret;
+}
+
+/**
+ * tracecmd_read_instance_file - Read from a trace file of specific instance.
+ * @instance: buffer instance for the file, can be NULL for the top instance
+ * @file: name of the file
+ * @psize: Returns the number of bytes read.
+ *
+ * Returns a pointer to a NULL terminated string, read from the file, or NULL in
+ * case of an error.
+ */
+char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
+				  char *file, int *psize)
+{
+	char buffer[BUFSIZ];
+	int size = 0;
+	char *path;
+	char *buf;
+	int fd;
+	int r;
+
+	path = tracecmd_get_instance_file(instance, file);
+	fd = open(path, O_RDONLY);
+	tracecmd_put_tracing_file(path);
+	if (fd < 0) {
+		warning("File %s not found", file);
+		return NULL;
+	}
+	do {
+		r = read(fd, buffer, BUFSIZ);
+		if (r <= 0)
+			continue;
+		if (size)
+			buf = realloc(buf, size+r+1);
+		else
+			buf = malloc(r+1);
+		if (!buf)
+			die("Failed to allocate instance file buffer");
+		memcpy(buf+size, buffer, r);
+		size += r;
+	} while (r);
+
+	buf[size] = '\0';
+	if (psize)
+		*psize = size;
+	return buf;
+}
+
+/**
+ * tracecmd_set_clock - Set the clock of ftrace event's timestamps, per instance.
+ * @instance: Pointer to ftrace instance, containing the desired clock.
+ * @old_clock: Optional, return the newly allocated string with the old clock.
+ *
+ */
+void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock)
+{
+	char *content;
+	char *str;
+
+	if (!instance->clock)
+		return;
+
+	/* The current clock is in brackets, reset it when we are done */
+	content = tracecmd_read_instance_file(instance, "trace_clock", NULL);
+
+	/* check if first clock is set */
+	if (*content == '[')
+		str = strtok(content+1, "]");
+	else {
+		str = strtok(content, "[");
+		if (!str)
+			die("Can not find clock in trace_clock");
+		str = strtok(NULL, "]");
+	}
+	if (old_clock)
+		*old_clock = strdup(str);
+
+	free(content);
+	tracecmd_write_instance_file(instance,
+				     "trace_clock", instance->clock, "clock");
+}
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index 49a80ae..4c70d9a 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -918,11 +918,6 @@ char *tracecmd_get_tracing_file(const char *name)
 	return file;
 }
 
-void tracecmd_put_tracing_file(char *name)
-{
-	free(name);
-}
-
 void __noreturn __vdie(const char *fmt, va_list ap)
 {
 	int ret = errno;
@@ -967,6 +962,38 @@ void __weak *malloc_or_die(unsigned int size)
 	return data;
 }
 
+
+static ssize_t __do_write(int fd, const void *data, size_t size)
+{
+	ssize_t tot = 0;
+	ssize_t w;
+
+	do {
+		w = write(fd, data + tot, size - tot);
+		tot += w;
+
+		if (!w)
+			break;
+		if (w < 0)
+			return w;
+	} while (tot != size);
+
+	return tot;
+}
+
+ssize_t
+__do_write_check(int fd, const void *data, size_t size)
+{
+	ssize_t ret;
+
+	ret = __do_write(fd, data, size);
+	if (ret < 0)
+		return ret;
+	if (ret != size)
+		return -1;
+
+	return 0;
+}
 #define LOG_BUF_SIZE 1024
 static void __plog(const char *prefix, const char *fmt, va_list ap, FILE *fp)
 {
@@ -1084,3 +1111,37 @@ int tracecmd_stack_tracer_status(int *status)
 	*status = num;
 	return 1; /* full success */
 }
+
+/*
+ * tracecmd_write_file - Write in trace file
+ * @file: Full name of the trace file.
+ * @str: Null terminated string, that will be written in the file.
+ * @type: Null terminated string, describing the current write operation.
+ *	  Used for logging purposes.
+ *
+ * Returns the number of written bytes, or -1 in case of an error
+ */
+int tracecmd_write_file(const char *file, const char *str, const char *type)
+{
+	char buf[BUFSIZ];
+	int ret;
+	int fd;
+
+	fd = open(file, O_WRONLY | O_TRUNC);
+	if (fd < 0)
+		die("opening to '%s'", file);
+	ret = write(fd, str, strlen(str));
+	close(fd);
+	if (ret < 0 && type) {
+		/* write failed */
+		fd = open(file, O_RDONLY);
+		if (fd < 0)
+			die("writing to '%s'", file);
+		/* the filter has the error */
+		while ((ret = read(fd, buf, BUFSIZ)) > 0)
+			fprintf(stderr, "%.*s", ret, buf);
+		die("Failed %s of %s\n", type, file);
+		close(fd);
+	}
+	return ret;
+}
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index fedc0b7..3a05e9c 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -181,7 +181,7 @@ struct pid_addr_maps {
 
 struct buffer_instance {
 	struct buffer_instance	*next;
-	const char		*name;
+	struct tracecmd_instance	*ftrace;
 	char			*cpumask;
 	struct event_list	*events;
 	struct event_list	**event_next;
@@ -195,7 +195,6 @@ struct buffer_instance {
 	struct func_list	*filter_funcs;
 	struct func_list	*notrace_funcs;
 
-	const char		*clock;
 	unsigned int		*client_ports;
 
 	struct trace_seq	*s_save;
@@ -225,6 +224,8 @@ struct buffer_instance {
 	bool			use_fifos;
 };
 
+void init_top_instance(void);
+
 extern struct buffer_instance top_instance;
 extern struct buffer_instance *buffer_instances;
 extern struct buffer_instance *first_instance;
@@ -238,7 +239,6 @@ extern struct buffer_instance *first_instance;
 
 struct buffer_instance *create_instance(const char *name);
 void add_instance(struct buffer_instance *instance, int cpu_count);
-char *get_instance_file(struct buffer_instance *instance, const char *file);
 void update_first_instance(struct buffer_instance *instance, int topt);
 
 void show_instance_file(struct buffer_instance *instance, const char *name);
diff --git a/tracecmd/trace-list.c b/tracecmd/trace-list.c
index 65099a5..e0782ed 100644
--- a/tracecmd/trace-list.c
+++ b/tracecmd/trace-list.c
@@ -33,7 +33,7 @@ void show_instance_file(struct buffer_instance *instance, const char *name)
 {
 	char *path;
 
-	path = get_instance_file(instance, name);
+	path = tracecmd_get_instance_file(instance->ftrace, name);
 	dump_file_content(path);
 	tracecmd_put_tracing_file(path);
 }
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 80b2234..13a7cda 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -173,7 +173,7 @@ static struct reset_file *reset_files;
 /* Triggers need to be cleared in a special way */
 static struct reset_file *reset_triggers;
 
-struct buffer_instance top_instance = { .flags = BUFFER_FL_KEEP };
+struct buffer_instance top_instance;
 struct buffer_instance *buffer_instances;
 struct buffer_instance *first_instance;
 
@@ -191,7 +191,7 @@ static inline int no_top_instance(void)
 	return first_instance != &top_instance;
 }
 
-static void init_instance(struct buffer_instance *instance)
+void tracecmd_init_instance(struct buffer_instance *instance)
 {
 	instance->event_next = &instance->events;
 }
@@ -318,7 +318,7 @@ static void reset_save_file_cond(const char *file, int prio,
  */
 void add_instance(struct buffer_instance *instance, int cpu_count)
 {
-	init_instance(instance);
+	tracecmd_init_instance(instance);
 	instance->next = buffer_instances;
 	if (first_instance == buffer_instances)
 		first_instance = instance;
@@ -371,7 +371,12 @@ struct buffer_instance *create_instance(const char *name)
 	if (!instance)
 		return NULL;
 	memset(instance, 0, sizeof(*instance));
-	instance->name = name;
+
+	instance->ftrace = tracecmd_create_instance(name);
+	if (!instance->ftrace) {
+		free(instance);
+		return NULL;
+	}
 
 	return instance;
 }
@@ -471,7 +476,7 @@ void tracecmd_stat_cpu_instance(struct buffer_instance *instance,
 		return;
 	snprintf(file, 40, "per_cpu/cpu%d/stats", cpu);
 
-	path = get_instance_file(instance, file);
+	path = tracecmd_get_instance_file(instance->ftrace, file);
 	free(file);
 	fd = open(path, O_RDONLY);
 	tracecmd_put_tracing_file(path);
@@ -505,12 +510,12 @@ static void add_event(struct buffer_instance *instance, struct event_list *event
 static void reset_event_list(struct buffer_instance *instance)
 {
 	instance->events = NULL;
-	init_instance(instance);
+	tracecmd_init_instance(instance);
 }
 
 static char *get_temp_file(struct buffer_instance *instance, int cpu)
 {
-	const char *name = instance->name;
+	const char *name = instance->ftrace->name;
 	char *file = NULL;
 	int size;
 
@@ -557,7 +562,7 @@ static void put_temp_file(char *file)
 
 static void delete_temp_file(struct buffer_instance *instance, int cpu)
 {
-	const char *name = instance->name;
+	const char *name = instance->ftrace->name;
 	char file[PATH_MAX];
 
 	if (name)
@@ -783,96 +788,6 @@ static int set_ftrace(int set, int use_proc)
 	return 0;
 }
 
-/**
- * get_instance_file - return the path to a instance file.
- * @instance: buffer instance for the file
- * @file: name of file to return
- *
- * Returns the path name of the @file for the given @instance.
- *
- * Must use tracecmd_put_tracing_file() to free the returned string.
- */
-char *
-get_instance_file(struct buffer_instance *instance, const char *file)
-{
-	char *buf;
-	char *path;
-	int ret;
-
-	if (instance->name) {
-		ret = asprintf(&buf, "instances/%s/%s", instance->name, file);
-		if (ret < 0)
-			die("Failed to allocate name for %s/%s", instance->name, file);
-		path = tracecmd_get_tracing_file(buf);
-		free(buf);
-	} else
-		path = tracecmd_get_tracing_file(file);
-
-	return path;
-}
-
-static char *
-get_instance_dir(struct buffer_instance *instance)
-{
-	char *buf;
-	char *path;
-	int ret;
-
-	/* only works for instances */
-	if (!instance->name)
-		return NULL;
-
-	ret = asprintf(&buf, "instances/%s", instance->name);
-	if (ret < 0)
-		die("Failed to allocate for instance %s", instance->name);
-	path = tracecmd_get_tracing_file(buf);
-	free(buf);
-
-	return path;
-}
-
-static int write_file(const char *file, const char *str, const char *type)
-{
-	char buf[BUFSIZ];
-	int fd;
-	int ret;
-
-	fd = open(file, O_WRONLY | O_TRUNC);
-	if (fd < 0)
-		die("opening to '%s'", file);
-	ret = write(fd, str, strlen(str));
-	close(fd);
-	if (ret < 0 && type) {
-		/* write failed */
-		fd = open(file, O_RDONLY);
-		if (fd < 0)
-			die("writing to '%s'", file);
-		/* the filter has the error */
-		while ((ret = read(fd, buf, BUFSIZ)) > 0)
-			fprintf(stderr, "%.*s", ret, buf);
-		die("Failed %s of %s\n", type, file);
-		close(fd);
-	}
-	return ret;
-}
-
-static int
-write_instance_file(struct buffer_instance *instance,
-		    const char *file, const char *str, const char *type)
-{
-	struct stat st;
-	char *path;
-	int ret;
-
-	path = get_instance_file(instance, file);
-	ret = stat(path, &st);
-	if (ret == 0)
-		ret = write_file(path, str, type);
-	tracecmd_put_tracing_file(path);
-
-	return ret;
-}
-
 static void __clear_trace(struct buffer_instance *instance)
 {
 	FILE *fp;
@@ -882,7 +797,7 @@ static void __clear_trace(struct buffer_instance *instance)
 		return;
 
 	/* reset the trace */
-	path = get_instance_file(instance, "trace");
+	path = tracecmd_get_instance_file(instance->ftrace, "trace");
 	fp = fopen(path, "w");
 	if (!fp)
 		die("writing to '%s'", path);
@@ -916,8 +831,8 @@ static void clear_trace(void)
 
 static void reset_max_latency(struct buffer_instance *instance)
 {
-	 write_instance_file(instance,
-			     "tracing_max_latency", "0", "max_latency");
+	tracecmd_write_instance_file(instance->ftrace,
+				     "tracing_max_latency", "0", "max_latency");
 }
 
 static void add_filter_pid(int pid, int exclude)
@@ -1363,7 +1278,8 @@ static void add_event_pid(const char *buf)
 	struct buffer_instance *instance;
 
 	for_all_instances(instance)
-		write_instance_file(instance, "set_event_pid", buf, "event_pid");
+		tracecmd_write_instance_file(instance->ftrace,
+					     "set_event_pid", buf, "event_pid");
 }
 
 static void add_new_filter_pid(int pid)
@@ -1592,7 +1508,7 @@ set_plugin_instance(struct buffer_instance *instance, const char *name)
 	if (is_guest(instance))
 		return;
 
-	path = get_instance_file(instance, "current_tracer");
+	path = tracecmd_get_instance_file(instance->ftrace, "current_tracer");
 	fp = fopen(path, "w");
 	if (!fp) {
 		/*
@@ -1616,7 +1532,7 @@ set_plugin_instance(struct buffer_instance *instance, const char *name)
 
 	/* Make sure func_stack_trace option is disabled */
 	/* First try instance file, then top level */
-	path = get_instance_file(instance, "options/func_stack_trace");
+	path = tracecmd_get_instance_file(instance->ftrace, "options/func_stack_trace");
 	fp = fopen(path, "w");
 	if (!fp) {
 		tracecmd_put_tracing_file(path);
@@ -1677,8 +1593,6 @@ static int set_option(const char *option)
 	return 0;
 }
 
-static char *read_instance_file(struct buffer_instance *instance, char *file, int *psize);
-
 static void disable_func_stack_trace_instance(struct buffer_instance *instance)
 {
 	struct stat st;
@@ -1691,13 +1605,13 @@ static void disable_func_stack_trace_instance(struct buffer_instance *instance)
 	if (is_guest(instance))
 		return;
 
-	path = get_instance_file(instance, "current_tracer");
+	path = tracecmd_get_instance_file(instance->ftrace, "current_tracer");
 	ret = stat(path, &st);
 	tracecmd_put_tracing_file(path);
 	if (ret < 0)
 		return;
 
-	content = read_instance_file(instance, "current_tracer", &size);
+	content = tracecmd_read_instance_file(instance->ftrace, "current_tracer", &size);
 	cond = strstrip(content);
 	if (memcmp(cond, "function", size - (cond - content)) !=0)
 		goto out;
@@ -1850,7 +1764,7 @@ static int trace_check_file_exists(struct buffer_instance *instance, char *file)
 	char *path;
 	int ret;
 
-	path = get_instance_file(instance, file);
+	path = tracecmd_get_instance_file(instance->ftrace, file);
 	ret = stat(path, &st);
 	tracecmd_put_tracing_file(path);
 
@@ -1929,7 +1843,7 @@ reset_events_instance(struct buffer_instance *instance)
 	}
 
 	c = '0';
-	path = get_instance_file(instance, "events/enable");
+	path = tracecmd_get_instance_file(instance->ftrace, "events/enable");
 	fd = open(path, O_WRONLY);
 	if (fd < 0)
 		die("opening to '%s'", path);
@@ -1937,7 +1851,7 @@ reset_events_instance(struct buffer_instance *instance)
 	close(fd);
 	tracecmd_put_tracing_file(path);
 
-	path = get_instance_file(instance, "events/*/filter");
+	path = tracecmd_get_instance_file(instance->ftrace, "events/*/filter");
 	globbuf.gl_offs = 0;
 	ret = glob(path, 0, NULL, &globbuf);
 	tracecmd_put_tracing_file(path);
@@ -2009,7 +1923,7 @@ static int find_trigger(const char *file, char *buf, int size, int fields)
 
 static void write_filter(const char *file, const char *filter)
 {
-	write_file(file, filter, "filter");
+	tracecmd_write_file(file, filter, "filter");
 }
 
 static void clear_filter(const char *file)
@@ -2019,12 +1933,12 @@ static void clear_filter(const char *file)
 
 static void write_trigger(const char *file, const char *trigger)
 {
-	write_file(file, trigger, "trigger");
+	tracecmd_write_file(file, trigger, "trigger");
 }
 
 static void write_func_filter(const char *file, const char *trigger)
 {
-	write_file(file, trigger, "function filter");
+	tracecmd_write_file(file, trigger, "function filter");
 }
 
 static void clear_trigger(const char *file)
@@ -2117,7 +2031,7 @@ static void update_reset_files(void)
 		reset_files = reset->next;
 
 		if (!keep)
-			write_file(reset->path, reset->reset, "reset");
+			tracecmd_write_file(reset->path, reset->reset, "reset");
 		free(reset->path);
 		free(reset->reset);
 		free(reset);
@@ -2198,7 +2112,7 @@ static int open_instance_fd(struct buffer_instance *instance,
 	int fd;
 	char *path;
 
-	path = get_instance_file(instance, file);
+	path = tracecmd_get_instance_file(instance->ftrace, file);
 	fd = open(path, flags);
 	if (fd < 0) {
 		/* instances may not be created yet */
@@ -2227,7 +2141,7 @@ static int open_tracing_on(struct buffer_instance *instance)
 	return fd;
 }
 
-static void write_tracing_on(struct buffer_instance *instance, int on)
+void write_tracing_on(struct buffer_instance *instance, int on)
 {
 	int ret;
 	int fd;
@@ -2507,7 +2421,7 @@ static void set_mask(struct buffer_instance *instance)
 	if (!instance->cpumask)
 		return;
 
-	path = get_instance_file(instance, "tracing_cpumask");
+	path = tracecmd_get_instance_file(instance->ftrace, "tracing_cpumask");
 	if (!path)
 		die("could not allocate path");
 	reset_save_file(path, RESET_DEFAULT_PRIO);
@@ -2555,39 +2469,6 @@ void tracecmd_enable_events(void)
 	enable_events(first_instance);
 }
 
-static void set_clock(struct buffer_instance *instance)
-{
-	char *path;
-	char *content;
-	char *str;
-
-	if (is_guest(instance))
-		return;
-
-	if (!instance->clock)
-		return;
-
-	/* The current clock is in brackets, reset it when we are done */
-	content = read_instance_file(instance, "trace_clock", NULL);
-
-	/* check if first clock is set */
-	if (*content == '[')
-		str = strtok(content+1, "]");
-	else {
-		str = strtok(content, "[");
-		if (!str)
-			die("Can not find clock in trace_clock");
-		str = strtok(NULL, "]");
-	}
-	path = get_instance_file(instance, "trace_clock");
-	add_reset_file(path, str, RESET_DEFAULT_PRIO);
-
-	free(content);
-	tracecmd_put_tracing_file(path);
-
-	write_instance_file(instance, "trace_clock", instance->clock, "clock");
-}
-
 static void set_max_graph_depth(struct buffer_instance *instance, char *max_graph_depth)
 {
 	char *path;
@@ -2596,16 +2477,16 @@ static void set_max_graph_depth(struct buffer_instance *instance, char *max_grap
 	if (is_guest(instance))
 		return;
 
-	path = get_instance_file(instance, "max_graph_depth");
+	path = tracecmd_get_instance_file(instance->ftrace, "max_graph_depth");
 	reset_save_file(path, RESET_DEFAULT_PRIO);
 	tracecmd_put_tracing_file(path);
-	ret = write_instance_file(instance, "max_graph_depth", max_graph_depth,
-				  NULL);
+	ret = tracecmd_write_instance_file(instance->ftrace,
+					   "max_graph_depth", max_graph_depth,
+					   NULL);
 	if (ret < 0)
 		die("could not write to max_graph_depth");
 }
 
-
 /**
  * create_event - create and event descriptor
  * @instance: instance to use
@@ -2717,7 +2598,7 @@ static int expand_event_files(struct buffer_instance *instance,
 	if (ret < 0)
 		die("Failed to allocate event filter path for %s", file);
 
-	path = get_instance_file(instance, p);
+	path = tracecmd_get_instance_file(instance->ftrace, p);
 
 	globbuf.gl_offs = 0;
 	ret = glob(path, 0, NULL, &globbuf);
@@ -3179,10 +3060,7 @@ create_recorder_instance_pipe(struct buffer_instance *instance,
 	unsigned flags = recorder_flags | TRACECMD_RECORD_BLOCK;
 	char *path;
 
-	if (instance->name)
-		path = get_instance_dir(instance);
-	else
-		path = tracecmd_find_tracing_dir();
+	path = tracecmd_get_instance_dir(instance->ftrace);
 
 	if (!path)
 		die("malloc");
@@ -3192,7 +3070,7 @@ create_recorder_instance_pipe(struct buffer_instance *instance,
 
 	recorder = tracecmd_create_buffer_recorder_fd(brass[1], cpu, flags, path);
 
-	if (instance->name)
+	if (instance->ftrace->name)
 		tracecmd_put_tracing_file(path);
 
 	return recorder;
@@ -3227,10 +3105,10 @@ create_recorder_instance(struct buffer_instance *instance, const char *file, int
 	if (brass)
 		return create_recorder_instance_pipe(instance, cpu, brass);
 
-	if (!instance->name)
+	if (!instance->ftrace->name)
 		return tracecmd_create_recorder_maxkb(file, cpu, recorder_flags, max_kb);
 
-	path = get_instance_dir(instance);
+	path = tracecmd_get_instance_dir(instance->ftrace);
 
 	record = tracecmd_create_buffer_recorder_maxkb(file, cpu, recorder_flags,
 						       path, max_kb);
@@ -3284,8 +3162,8 @@ static int create_recorder(struct buffer_instance *instance, int cpu,
 		}
 		if (fd < 0)
 			die("Failed connecting to client");
-		if (instance->name && !is_agent(instance))
-			path = get_instance_dir(instance);
+		if (instance->ftrace->name && !is_agent(instance))
+			path = tracecmd_get_instance_dir(instance->ftrace);
 		else
 			path = tracecmd_find_tracing_dir();
 		recorder = tracecmd_create_buffer_recorder_fd(fd, cpu, flags, path);
@@ -3598,7 +3476,7 @@ static void connect_to_agent(struct buffer_instance *instance)
 	bool use_fifos = false;
 
 	if (!no_fifos) {
-		nr_fifos = open_guest_fifos(instance->name, &fds);
+		nr_fifos = open_guest_fifos(instance->ftrace->name, &fds);
 		use_fifos = nr_fifos > 0;
 	}
 
@@ -3625,7 +3503,7 @@ static void connect_to_agent(struct buffer_instance *instance)
 		if (nr_cpus != nr_fifos) {
 			warning("number of FIFOs (%d) for guest %s differs "
 				"from number of virtual CPUs (%d)",
-				nr_fifos, instance->name, nr_cpus);
+				nr_fifos, instance->ftrace->name, nr_cpus);
 			nr_cpus = nr_cpus < nr_fifos ? nr_cpus : nr_fifos;
 		}
 		free(ports);
@@ -3651,7 +3529,7 @@ static void setup_guest(struct buffer_instance *instance)
 	int fd;
 
 	/* Create a place to store the guest meta data */
-	file = get_guest_file(output_file, instance->name);
+	file = get_guest_file(output_file, instance->ftrace->name);
 	if (!file)
 		die("Failed to allocate memory");
 
@@ -3827,7 +3705,7 @@ add_buffer_stat(struct tracecmd_output *handle, struct buffer_instance *instance
 	int i;
 
 	trace_seq_init(&s);
-	trace_seq_printf(&s, "\nBuffer: %s\n\n", instance->name);
+	trace_seq_printf(&s, "\nBuffer: %s\n\n", instance->ftrace->name);
 	tracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT,
 			    s.len+1, s.buffer);
 	trace_seq_destroy(&s);
@@ -3893,7 +3771,7 @@ static void print_stat(struct buffer_instance *instance)
 		return;
 
 	if (!is_top_instance(instance))
-		printf("\nBuffer: %s\n\n", instance->name);
+		printf("\nBuffer: %s\n\n", instance->ftrace->name);
 
 	for (cpu = 0; cpu < instance->cpu_count; cpu++)
 		trace_seq_do_printf(&instance->s_print[cpu]);
@@ -3933,7 +3811,7 @@ static void write_guest_file(struct buffer_instance *instance)
 	char **temp_files;
 	int i, fd;
 
-	file = get_guest_file(output_file, instance->name);
+	file = get_guest_file(output_file, instance->ftrace->name);
 	if (!file)
 		die("Failed to allocate memory");
 
@@ -4049,7 +3927,7 @@ static void record_data(struct common_record_context *ctx)
 					continue;
 
 				buffer_options[i++] = tracecmd_add_buffer_option(handle,
-										 instance->name,
+										 instance->ftrace->name,
 										 cpus);
 				add_buffer_stat(handle, instance);
 			}
@@ -4096,7 +3974,7 @@ static int write_func_file(struct buffer_instance *instance,
 	if (!*list)
 		return 0;
 
-	path = get_instance_file(instance, file);
+	path = tracecmd_get_instance_file(instance->ftrace, file);
 
 	fd = open(path, O_WRONLY | O_TRUNC);
 	if (fd < 0)
@@ -4140,7 +4018,7 @@ static int functions_filtered(struct buffer_instance *instance)
 	char *path;
 	int fd;
 
-	path = get_instance_file(instance, "set_ftrace_filter");
+	path = tracecmd_get_instance_file(instance->ftrace, "set_ftrace_filter");
 	fd = open(path, O_RDONLY);
 	tracecmd_put_tracing_file(path);
 	if (fd < 0) {
@@ -4148,7 +4026,7 @@ static int functions_filtered(struct buffer_instance *instance)
 			warning("Can not set set_ftrace_filter");
 		else
 			warning("Can not set set_ftrace_filter for %s",
-				instance->name);
+				instance->ftrace->name);
 		return 0;
 	}
 
@@ -4308,45 +4186,10 @@ static unsigned long long find_time_stamp(struct tep_handle *pevent)
 	return ts;
 }
 
-static char *read_instance_file(struct buffer_instance *instance, char *file, int *psize)
-{
-	char buffer[BUFSIZ];
-	char *path;
-	char *buf;
-	int size = 0;
-	int fd;
-	int r;
-
-	path = get_instance_file(instance, file);
-	fd = open(path, O_RDONLY);
-	tracecmd_put_tracing_file(path);
-	if (fd < 0) {
-		warning("%s not found, --date ignored", file);
-		return NULL;
-	}
-	do {
-		r = read(fd, buffer, BUFSIZ);
-		if (r <= 0)
-			continue;
-		if (size)
-			buf = realloc(buf, size+r+1);
-		else
-			buf = malloc(r+1);
-		if (!buf)
-			die("Failed to allocate instance file buffer");
-		memcpy(buf+size, buffer, r);
-		size += r;
-	} while (r);
-
-	buf[size] = '\0';
-	if (psize)
-		*psize = size;
-	return buf;
-}
 
 static char *read_file(char *file, int *psize)
 {
-	return read_instance_file(&top_instance, file, psize);
+	return tracecmd_read_instance_file(top_instance.ftrace, file, psize);
 }
 
 /*
@@ -4479,7 +4322,7 @@ static void set_buffer_size_instance(struct buffer_instance *instance)
 
 	snprintf(buf, BUFSIZ, "%d", buffer_size);
 
-	path = get_instance_file(instance, "buffer_size_kb");
+	path = tracecmd_get_instance_file(instance->ftrace, "buffer_size_kb");
 	fd = open(path, O_WRONLY);
 	if (fd < 0) {
 		warning("can't open %s", path);
@@ -4540,7 +4383,7 @@ static void clear_instance_triggers(struct buffer_instance *instance)
 	enum event_iter_type type;
 	enum event_process processed = PROCESSED_NONE;
 
-	path = get_instance_file(instance, "events");
+	path = tracecmd_get_instance_file(instance->ftrace, "events");
 	if (!path)
 		die("malloc");
 
@@ -4601,7 +4444,7 @@ static void clear_instance_filters(struct buffer_instance *instance)
 	enum event_iter_type type;
 	enum event_process processed = PROCESSED_NONE;
 
-	path = get_instance_file(instance, "events");
+	path = tracecmd_get_instance_file(instance->ftrace, "events");
 	if (!path)
 		die("malloc");
 
@@ -4637,7 +4480,8 @@ static void reset_clock(void)
 	struct buffer_instance *instance;
 
 	for_all_instances(instance)
-		write_instance_file(instance, "trace_clock", "local", "clock");
+		tracecmd_write_instance_file(instance->ftrace,
+					    "trace_clock", "local", "clock");
 }
 
 static void reset_cpu_mask(void)
@@ -4656,7 +4500,8 @@ static void reset_cpu_mask(void)
 		strcat(buf, ",ffffffff");
 
 	for_all_instances(instance)
-		write_instance_file(instance, "tracing_cpumask", buf, "cpumask");
+		tracecmd_write_instance_file(instance->ftrace,
+					     "tracing_cpumask", buf, "cpumask");
 }
 
 static void reset_event_pid(void)
@@ -4685,7 +4530,7 @@ static void clear_func_filters(void)
 
 	for_all_instances(instance) {
 		for (i = 0; files[i]; i++) {
-			path = get_instance_file(instance, files[i]);
+			path = tracecmd_get_instance_file(instance->ftrace, files[i]);
 			clear_func_filter(path);
 			tracecmd_put_tracing_file(path);
 		}
@@ -4695,32 +4540,20 @@ static void clear_func_filters(void)
 static void make_instances(void)
 {
 	struct buffer_instance *instance;
-	struct stat st;
-	char *path;
-	int ret;
 
 	for_each_instance(instance) {
 		if (is_guest(instance))
 			continue;
-
-		path = get_instance_dir(instance);
-		ret = stat(path, &st);
-		if (ret < 0) {
-			ret = mkdir(path, 0777);
-			if (ret < 0)
-				die("mkdir %s", path);
-		} else
+		if (tracecmd_make_instance(instance->ftrace) > 0) {
 			/* Don't delete instances that already exist */
 			instance->flags |= BUFFER_FL_KEEP;
-		tracecmd_put_tracing_file(path);
+		}
 	}
 }
 
 void tracecmd_remove_instances(void)
 {
 	struct buffer_instance *instance;
-	char *path;
-	int ret;
 
 	for_each_instance(instance) {
 		/* Only delete what we created */
@@ -4730,11 +4563,7 @@ void tracecmd_remove_instances(void)
 			close(instance->tracing_on_fd);
 			instance->tracing_on_fd = 0;
 		}
-		path = get_instance_dir(instance);
-		ret = rmdir(path);
-		if (ret < 0)
-			die("rmdir %s", path);
-		tracecmd_put_tracing_file(path);
+		tracecmd_remove_instance(instance->ftrace);
 	}
 }
 
@@ -5028,7 +4857,7 @@ static int test_stacktrace_trigger(struct buffer_instance *instance)
 	int ret = 0;
 	int fd;
 
-	path = get_instance_file(instance, "events/sched/sched_switch/trigger");
+	path = tracecmd_get_instance_file(instance->ftrace, "events/sched/sched_switch/trigger");
 
 	clear_trigger(path);
 
@@ -5210,6 +5039,15 @@ void update_first_instance(struct buffer_instance *instance, int topt)
 		first_instance = buffer_instances;
 }
 
+void init_top_instance(void)
+{
+	if (!top_instance.ftrace)
+		top_instance.ftrace = tracecmd_create_instance(NULL);
+	top_instance.cpu_count = count_cpus();
+	top_instance.flags = BUFFER_FL_KEEP;
+	tracecmd_init_instance(&top_instance);
+}
+
 enum {
 	OPT_user		= 243,
 	OPT_procmap		= 244,
@@ -5234,7 +5072,7 @@ void trace_stop(int argc, char **argv)
 	int topt = 0;
 	struct buffer_instance *instance = &top_instance;
 
-	init_instance(instance);
+	init_top_instance();
 
 	for (;;) {
 		int c;
@@ -5275,7 +5113,7 @@ void trace_restart(int argc, char **argv)
 	int topt = 0;
 	struct buffer_instance *instance = &top_instance;
 
-	init_instance(instance);
+	init_top_instance();
 
 	for (;;) {
 		int c;
@@ -5317,7 +5155,7 @@ void trace_reset(int argc, char **argv)
 	int topt = 0;
 	struct buffer_instance *instance = &top_instance;
 
-	init_instance(instance);
+	init_top_instance();
 
 	/* if last arg is -a, then -b and -d apply to all instances */
 	int last_specified_all = 0;
@@ -5402,9 +5240,8 @@ static void init_common_record_context(struct common_record_context *ctx,
 	memset(ctx, 0, sizeof(*ctx));
 	ctx->instance = &top_instance;
 	ctx->curr_cmd = curr_cmd;
-	init_instance(ctx->instance);
 	local_cpu_count = count_cpus();
-	ctx->instance->cpu_count = local_cpu_count;
+	init_top_instance();
 }
 
 #define IS_EXTRACT(ctx) ((ctx)->curr_cmd == CMD_extract)
@@ -5642,7 +5479,7 @@ static void parse_record_options(int argc,
 			}
 			break;
 		case 'C':
-			ctx->instance->clock = optarg;
+			ctx->instance->ftrace->clock = optarg;
 			break;
 		case 'v':
 			neg_event = 1;
@@ -5964,6 +5801,23 @@ static bool has_local_instances(void)
 	return false;
 }
 
+static void set_clock(struct buffer_instance *instance)
+{
+	char *old_clock = NULL;
+	char *path;
+
+	if (is_guest(instance))
+		return;
+
+	tracecmd_set_clock(instance->ftrace, &old_clock);
+	if (old_clock) {
+		path = tracecmd_get_instance_file(instance->ftrace, "trace_clock");
+		if (path)
+			add_reset_file(path, old_clock, RESET_DEFAULT_PRIO);
+		free(old_clock);
+		tracecmd_put_tracing_file(path);
+	}
+}
 /*
  * This function contains common code for the following commands:
  * record, start, stream, profile.
@@ -6015,7 +5869,6 @@ static void record_trace(int argc, char **argv,
 
 	for_all_instances(instance)
 		set_clock(instance);
-
 	/* Record records the date first */
 	if (ctx->date &&
 	    ((IS_RECORD(ctx) && has_local_instances()) || IS_RECORD_AGENT(ctx)))
diff --git a/tracecmd/trace-show.c b/tracecmd/trace-show.c
index 96bfe77..ca0f4cf 100644
--- a/tracecmd/trace-show.c
+++ b/tracecmd/trace-show.c
@@ -51,6 +51,8 @@ void trace_show(int argc, char **argv)
 		{NULL, 0, NULL, 0}
 	};
 
+	init_top_instance();
+
 	while ((c = getopt_long(argc-1, argv+1, "B:c:fsp",
 				long_options, &option_index)) >= 0) {
 		switch (c) {
diff --git a/tracecmd/trace-stat.c b/tracecmd/trace-stat.c
index 538f4ad..32aebc5 100644
--- a/tracecmd/trace-stat.c
+++ b/tracecmd/trace-stat.c
@@ -30,7 +30,7 @@ static int get_instance_file_fd(struct buffer_instance *instance,
 	char *path;
 	int fd;
 
-	path = get_instance_file(instance, file);
+	path = tracecmd_get_instance_file(instance->ftrace, file);
 	fd = open(path, O_RDONLY);
 	tracecmd_put_tracing_file(path);
 
@@ -347,7 +347,7 @@ static void report_events(struct buffer_instance *instance)
 
 	free(str);
 
-	path = get_instance_file(instance, "events");
+	path = tracecmd_get_instance_file(instance->ftrace, "events");
 	if (!path)
 		die("malloc");
 
@@ -436,7 +436,7 @@ static void report_event_filters(struct buffer_instance *instance)
 	enum event_iter_type type;
 	enum event_process processed = PROCESSED_NONE;
 
-	path = get_instance_file(instance, "events");
+	path = tracecmd_get_instance_file(instance->ftrace, "events");
 	if (!path)
 		die("malloc");
 
@@ -509,7 +509,7 @@ static void report_event_triggers(struct buffer_instance *instance)
 	enum event_iter_type type;
 	enum event_process processed = PROCESSED_NONE;
 
-	path = get_instance_file(instance, "events");
+	path = tracecmd_get_instance_file(instance->ftrace, "events");
 	if (!path)
 		die("malloc");
 
@@ -598,7 +598,7 @@ static void report_graph_funcs(struct buffer_instance *instance)
 {
 	char *path;
 
-	path = get_instance_file(instance, "set_graph_function");
+	path = tracecmd_get_instance_file(instance->ftrace, "set_graph_function");
 	if (!path)
 		die("malloc");
 
@@ -606,7 +606,7 @@ static void report_graph_funcs(struct buffer_instance *instance)
 	
 	tracecmd_put_tracing_file(path);
 
-	path = get_instance_file(instance, "set_graph_notrace");
+	path = tracecmd_get_instance_file(instance->ftrace, "set_graph_notrace");
 	if (!path)
 		die("malloc");
 
@@ -619,7 +619,7 @@ static void report_ftrace_filters(struct buffer_instance *instance)
 {
 	char *path;
 
-	path = get_instance_file(instance, "set_ftrace_filter");
+	path = tracecmd_get_instance_file(instance->ftrace, "set_ftrace_filter");
 	if (!path)
 		die("malloc");
 
@@ -627,7 +627,7 @@ static void report_ftrace_filters(struct buffer_instance *instance)
 	
 	tracecmd_put_tracing_file(path);
 
-	path = get_instance_file(instance, "set_ftrace_notrace");
+	path = tracecmd_get_instance_file(instance->ftrace, "set_ftrace_notrace");
 	if (!path)
 		die("malloc");
 
@@ -857,7 +857,7 @@ static void stat_instance(struct buffer_instance *instance)
 	if (instance != &top_instance) {
 		if (instance != first_instance)
 			printf("---------------\n");
-		printf("Instance: %s\n", instance->name);
+		printf("Instance: %s\n", instance->ftrace->name);
 	}
 
 	report_plugin(instance);
@@ -882,6 +882,8 @@ void trace_stat (int argc, char **argv)
 	int status;
 	int c;
 
+	init_top_instance();
+
 	for (;;) {
 		c = getopt(argc-1, argv+1, "tB:");
 		if (c == -1)
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 05/18] trace-cmd: Add new library API for local CPU count
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (3 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-04 20:09   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 06/18] trace-cmd: Add new library API for reading ftrace buffers Tzvetomir Stoyanov (VMware)
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

In order to reuse the code, the function detecting the
number of local CPU is moved from trace-cmd application to
libtracecmd. The following new library API is introduced:
	int tracecmd_count_cpus(void);

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h  |  3 +++
 lib/trace-cmd/trace-util.c     | 40 +++++++++++++++++++++++++++++
 tracecmd/include/trace-local.h |  2 --
 tracecmd/trace-agent.c         |  2 +-
 tracecmd/trace-profile.c       |  2 +-
 tracecmd/trace-record.c        | 46 +++-------------------------------
 tracecmd/trace-stat.c          |  4 +--
 7 files changed, 50 insertions(+), 49 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 5287d23..5d4292e 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -427,6 +427,9 @@ void tracecmd_plog(const char *fmt, ...);
 void tracecmd_plog_error(const char *fmt, ...);
 int tracecmd_set_logfile(char *logfile);
 
+/* --- System --- */
+int tracecmd_count_cpus(void);
+
 /* --- Hack! --- */
 int tracecmd_blk_hack(struct tracecmd_input *handle);
 
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index 4c70d9a..e019dce 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -1112,6 +1112,46 @@ int tracecmd_stack_tracer_status(int *status)
 	return 1; /* full success */
 }
 
+int tracecmd_count_cpus(void)
+{
+	FILE *fp;
+	char buf[1024];
+	int cpus = 0;
+	char *pbuf;
+	size_t *pn;
+	size_t n;
+	int r;
+
+	cpus = sysconf(_SC_NPROCESSORS_CONF);
+	if (cpus > 0)
+		return cpus;
+
+	warning("sysconf could not determine number of CPUS");
+
+	/* Do the hack to figure out # of CPUS */
+	n = 1024;
+	pn = &n;
+	pbuf = buf;
+
+	fp = fopen("/proc/cpuinfo", "r");
+	if (!fp)
+		die("Can not read cpuinfo");
+
+	while ((r = getline(&pbuf, pn, fp)) >= 0) {
+		char *p;
+
+		if (strncmp(buf, "processor", 9) != 0)
+			continue;
+		for (p = buf+9; isspace(*p); p++)
+			;
+		if (*p == ':')
+			cpus++;
+	}
+	fclose(fp);
+
+	return cpus;
+}
+
 /*
  * tracecmd_write_file - Write in trace file
  * @file: Full name of the trace file.
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index 3a05e9c..a51f0d0 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -243,8 +243,6 @@ void update_first_instance(struct buffer_instance *instance, int topt);
 
 void show_instance_file(struct buffer_instance *instance, const char *name);
 
-int count_cpus(void);
-
 /* moved from trace-cmd.h */
 void tracecmd_create_top_instance(char *name);
 void tracecmd_remove_instances(void);
diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
index 93e3edf..1c6e0a3 100644
--- a/tracecmd/trace-agent.c
+++ b/tracecmd/trace-agent.c
@@ -203,7 +203,7 @@ static void agent_serve(unsigned int port)
 
 	signal(SIGCHLD, handle_sigchld);
 
-	nr_cpus = count_cpus();
+	nr_cpus = tracecmd_count_cpus();
 	page_size = getpagesize();
 
 	sd = make_vsock(port);
diff --git a/tracecmd/trace-profile.c b/tracecmd/trace-profile.c
index 231e381..cfae2a2 100644
--- a/tracecmd/trace-profile.c
+++ b/tracecmd/trace-profile.c
@@ -1314,7 +1314,7 @@ void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hook,
 	 * system.
 	 */
 	if (!h->cpus)
-		h->cpus = count_cpus();
+		h->cpus = tracecmd_count_cpus();
 
 	list_head_init(&h->migrate_starts);
 	h->cpu_starts = malloc(sizeof(*h->cpu_starts) * h->cpus);
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 13a7cda..4459c90 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -2726,46 +2726,6 @@ static void expand_event_list(void)
 		expand_event_instance(instance);
 }
 
-int count_cpus(void)
-{
-	FILE *fp;
-	char buf[1024];
-	int cpus = 0;
-	char *pbuf;
-	size_t *pn;
-	size_t n;
-	int r;
-
-	cpus = sysconf(_SC_NPROCESSORS_CONF);
-	if (cpus > 0)
-		return cpus;
-
-	warning("sysconf could not determine number of CPUS");
-
-	/* Do the hack to figure out # of CPUS */
-	n = 1024;
-	pn = &n;
-	pbuf = buf;
-
-	fp = fopen("/proc/cpuinfo", "r");
-	if (!fp)
-		die("Can not read cpuinfo");
-
-	while ((r = getline(&pbuf, pn, fp)) >= 0) {
-		char *p;
-
-		if (strncmp(buf, "processor", 9) != 0)
-			continue;
-		for (p = buf+9; isspace(*p); p++)
-			;
-		if (*p == ':')
-			cpus++;
-	}
-	fclose(fp);
-
-	return cpus;
-}
-
 static void finish(int sig)
 {
 	/* all done */
@@ -4487,7 +4447,7 @@ static void reset_clock(void)
 static void reset_cpu_mask(void)
 {
 	struct buffer_instance *instance;
-	int cpus = count_cpus();
+	int cpus = tracecmd_count_cpus();
 	int fullwords = (cpus - 1) / 32;
 	int bits = (cpus - 1) % 32 + 1;
 	int len = (fullwords + 1) * 9;
@@ -5043,7 +5003,7 @@ void init_top_instance(void)
 {
 	if (!top_instance.ftrace)
 		top_instance.ftrace = tracecmd_create_instance(NULL);
-	top_instance.cpu_count = count_cpus();
+	top_instance.cpu_count = tracecmd_count_cpus();
 	top_instance.flags = BUFFER_FL_KEEP;
 	tracecmd_init_instance(&top_instance);
 }
@@ -5240,7 +5200,7 @@ static void init_common_record_context(struct common_record_context *ctx,
 	memset(ctx, 0, sizeof(*ctx));
 	ctx->instance = &top_instance;
 	ctx->curr_cmd = curr_cmd;
-	local_cpu_count = count_cpus();
+	local_cpu_count = tracecmd_count_cpus();
 	init_top_instance();
 }
 
diff --git a/tracecmd/trace-stat.c b/tracecmd/trace-stat.c
index 32aebc5..44ee559 100644
--- a/tracecmd/trace-stat.c
+++ b/tracecmd/trace-stat.c
@@ -737,7 +737,7 @@ static void report_cpumask(struct buffer_instance *instance)
 	cont = strstrip(str);
 
 	/* check to make sure all CPUs on this machine are set */
-	cpus = count_cpus();
+	cpus = tracecmd_count_cpus();
 
 	for (i = strlen(cont) - 1; i >= 0 && cpus > 0; i--) {
 		if (cont[i] == ',')
@@ -896,7 +896,7 @@ void trace_stat (int argc, char **argv)
 			instance = create_instance(optarg);
 			if (!instance)
 				die("Failed to create instance");
-			add_instance(instance, count_cpus());
+			add_instance(instance, tracecmd_count_cpus());
 			/* top instance requires direct access */
 			if (!topt && is_top_instance(first_instance))
 				first_instance = instance;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 06/18] trace-cmd: Add new library API for reading ftrace buffers
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (4 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 05/18] trace-cmd: Add new library API for local CPU count Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-04 21:10   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 07/18] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov (VMware)
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

A new libtracecmd API is introduced:

	int tracecmd_iterate_raw_events(struct tep_handle *tep,
					struct tracecmd_instance *instance,
					int (*callback)(struct tep_event *,
						   	struct tep_record *,
						   	int, void *context),
					void *callback_context);

It reads events from trace_pipe_raw, per cpu ftrace buffer, and calls a user
callback for each of them. The API is instance aware.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h |   8 +++
 lib/trace-cmd/trace-util.c    | 114 ++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 5d4292e..831bb1a 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -380,6 +380,14 @@ char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
 
 void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock);
 
+int tracecmd_iterate_raw_events(struct tep_handle *tep,
+				struct tracecmd_instance *instance,
+				   int (*callback)(struct tep_event *,
+						   struct tep_record *,
+						   int, void *context),
+				void *callback_context);
+
+
 
 /* --- Plugin handling --- */
 extern struct tep_plugin_option trace_ftrace_options[];
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index e019dce..a54a905 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -1185,3 +1185,117 @@ int tracecmd_write_file(const char *file, const char *str, const char *type)
 	}
 	return ret;
 }
+
+static int
+get_events_in_page(struct tep_handle *tep, void *page,
+		   int size, int cpu,
+		   int (*callback)(struct tep_event *,
+				   struct tep_record *,
+				   int, void *),
+		   void *callback_context)
+{
+	struct tep_record *last_record = NULL;
+	struct tep_event *event = NULL;
+	struct tep_record *record;
+	int id, cnt = 0;
+
+	if (size <= 0)
+		return 0;
+
+	while (true) {
+		event = NULL;
+		record = tracecmd_read_page_record(tep, page, size,
+						   last_record);
+		if (!record)
+			break;
+		free_record(last_record);
+		id = tep_data_type(tep, record);
+		event = tep_find_event(tep, id);
+		if (event && callback) {
+			if (callback(event, record, cpu, callback_context))
+				break;
+		}
+		last_record = record;
+	}
+	free_record(last_record);
+
+	return cnt;
+}
+
+/*
+ * tracecmd_iterate_raw_events - Iterate through events in trace_pipe_raw
+ *				 per CPU trace files
+ * @tep: a handle to the trace event parser context
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @callback: A user function, called for each record from the file.
+ * @callback_context: A custom context, passed to the user callback function
+ *
+ * If the @callback returns non-zero, the iteration stops.
+ *
+ * Returns -1 in case of an error, or 0 otherwise.
+ */
+int tracecmd_iterate_raw_events(struct tep_handle *tep,
+				struct tracecmd_instance *instance,
+				int (*callback)(struct tep_event *,
+						struct tep_record *,
+						int, void *),
+				void *callback_context)
+{
+	unsigned int p_size;
+	struct dirent *dent;
+	char file[PATH_MAX];
+	void *page = NULL;
+	struct stat st;
+	char *path;
+	DIR *dir;
+	int ret;
+	int cpu;
+	int fd;
+	int r;
+
+	p_size = getpagesize();
+	path = tracecmd_get_instance_file(instance, "per_cpu");
+	if (!path)
+		return -1;
+	dir = opendir(path);
+	if (!dir) {
+		ret = -1;
+		goto error;
+	}
+	page = malloc(p_size);
+	if (!page) {
+		ret = -1;
+		goto error;
+	}
+	while ((dent = readdir(dir))) {
+		const char *name = dent->d_name;
+
+		if (strlen(name) < 4 || strncmp(name, "cpu", 3) != 0)
+			continue;
+		cpu = atoi(name + 3);
+		sprintf(file, "%s/%s", path, name);
+		ret = stat(file, &st);
+		if (ret < 0 || !S_ISDIR(st.st_mode))
+			continue;
+
+		sprintf(file, "%s/%s/trace_pipe_raw", path, name);
+		fd = open(file, O_RDONLY | O_NONBLOCK);
+		if (fd < 0)
+			continue;
+		do {
+			r = read(fd, page, p_size);
+			if (r > 0)
+				get_events_in_page(tep, page, r, cpu,
+						   callback, callback_context);
+		} while (r > 0);
+		close(fd);
+	}
+	ret = 0;
+
+error:
+	if (dir)
+		closedir(dir);
+	free(page);
+	tracecmd_put_tracing_file(path);
+	return ret;
+}
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 07/18] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (5 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 06/18] trace-cmd: Add new library API for reading ftrace buffers Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-04 21:35   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 08/18] trace-cmd: Implement new API tracecmd_add_option_v() Tzvetomir Stoyanov (VMware)
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

In order to match host and guest events, a mapping between guest VCPU
and the host task, running this VCPU is needed. Extended existing
struct guest to hold such mapping and added logic in read_qemu_guests()
function to initialize it. Implemented a new internal API,
get_guest_vcpu_pid(), to retrieve VCPU-task mapping for given VM.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 tracecmd/include/trace-local.h |  2 ++
 tracecmd/trace-record.c        | 57 ++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index a51f0d0..51abef1 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -243,6 +243,8 @@ void update_first_instance(struct buffer_instance *instance, int topt);
 
 void show_instance_file(struct buffer_instance *instance, const char *name);
 
+int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu);
+
 /* moved from trace-cmd.h */
 void tracecmd_create_top_instance(char *name);
 void tracecmd_remove_instances(void);
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 4459c90..fd7ca82 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -2873,10 +2873,12 @@ static bool is_digits(const char *s)
 	return true;
 }
 
+#define VCPUS_MAX 256
 struct guest {
 	char *name;
 	int cid;
 	int pid;
+	int cpu_pid[VCPUS_MAX];
 };
 
 static struct guest *guests;
@@ -2894,6 +2896,46 @@ static char *get_qemu_guest_name(char *arg)
 	return arg;
 }
 
+static void read_qemu_guests_pids(char *guest_task, struct guest *guest)
+{
+	struct dirent *entry;
+	char path[PATH_MAX];
+	char *buf = NULL;
+	size_t n = 0;
+	int vcpu;
+	DIR *dir;
+	FILE *f;
+
+	snprintf(path, sizeof(path), "/proc/%s/task", guest_task);
+	dir = opendir(path);
+	if (!dir)
+		return;
+
+	while ((entry = readdir(dir))) {
+		if (!(entry->d_type == DT_DIR && is_digits(entry->d_name)))
+			continue;
+
+		snprintf(path, sizeof(path), "/proc/%s/task/%s/comm",
+			 guest_task, entry->d_name);
+		f = fopen(path, "r");
+		if (!f)
+			continue;
+		if (getline(&buf, &n, f) < 0)
+			goto next;
+		if (strncmp(buf, "CPU ", 4) != 0)
+			goto next;
+
+		vcpu = atoi(buf+4);
+		if (!(vcpu >= 0 && vcpu < VCPUS_MAX))
+			goto next;
+		guest->cpu_pid[vcpu] = atoi(entry->d_name);
+
+next:
+		fclose(f);
+	}
+	free(buf);
+}
+
 static void read_qemu_guests(void)
 {
 	static bool initialized;
@@ -2957,6 +2999,8 @@ static void read_qemu_guests(void)
 		if (!is_qemu)
 			goto next;
 
+		read_qemu_guests_pids(entry->d_name, &guest);
+
 		guests = realloc(guests, (guests_len + 1) * sizeof(*guests));
 		if (!guests)
 			die("Can not allocate guest buffer");
@@ -3002,6 +3046,19 @@ static char *parse_guest_name(char *guest, int *cid, int *port)
 	return guest;
 }
 
+int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu)
+{
+	int i;
+
+	if (!guests || guest_vcpu >= VCPUS_MAX)
+		return -1;
+
+	for (i = 0; i < guests_len; i++)
+		if (guest_cid == guests[i].cid)
+			return guests[i].cpu_pid[guest_vcpu];
+	return -1;
+}
+
 static void set_prio(int prio)
 {
 	struct sched_param sp;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 08/18] trace-cmd: Implement new API tracecmd_add_option_v()
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (6 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 07/18] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-04 21:47   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 09/18] trace-cmd: Add new API to generate a unique ID of the tracing session Tzvetomir Stoyanov (VMware)
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

A new tracecmd API tracecmd_add_option_v() is introduced.
It adds new option in trace.dat, similar to tracecmd_add_option(),
but the option's data is passed as list of buffers. The standard
struct iovec is used as input parameter, containing the option's
data buffers.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 include/trace-cmd/trace-cmd.h    |   5 ++
 include/traceevent/event-parse.h |   1 +
 lib/trace-cmd/trace-output.c     | 116 +++++++++++++++++++++++++++----
 3 files changed, 108 insertions(+), 14 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 831bb1a..47fa463 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -261,11 +261,16 @@ struct tracecmd_output *tracecmd_create_init_file_override(const char *output_fi
 struct tracecmd_option *tracecmd_add_option(struct tracecmd_output *handle,
 					    unsigned short id, int size,
 					    const void *data);
+struct tracecmd_option *
+tracecmd_add_option_v(struct tracecmd_output *handle,
+		      unsigned short id, const struct iovec *vector, int count);
+
 struct tracecmd_option *tracecmd_add_buffer_option(struct tracecmd_output *handle,
 						   const char *name, int cpus);
 
 int tracecmd_write_cpus(struct tracecmd_output *handle, int cpus);
 int tracecmd_write_options(struct tracecmd_output *handle);
+int tracecmd_append_options(struct tracecmd_output *handle);
 int tracecmd_update_option(struct tracecmd_output *handle,
 			   struct tracecmd_option *option, int size,
 			   const void *data);
diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
index a77af4c..30e06a7 100644
--- a/include/traceevent/event-parse.h
+++ b/include/traceevent/event-parse.h
@@ -11,6 +11,7 @@
 #include <stdio.h>
 #include <regex.h>
 #include <string.h>
+#include <sys/uio.h>
 
 #include "traceevent/trace-seq.h"
 
diff --git a/lib/trace-cmd/trace-output.c b/lib/trace-cmd/trace-output.c
index be4d3f5..db65ee4 100644
--- a/lib/trace-cmd/trace-output.c
+++ b/lib/trace-cmd/trace-output.c
@@ -950,21 +950,27 @@ static struct tracecmd_output *create_file(const char *output_file,
 }
 
 /**
- * tracecmd_add_option - add options to the file
+ * tracecmd_add_option_v - add options to the file
  * @handle: the output file handle name
  * @id: the id of the option
  * @size: the size of the option data
  * @data: the data to write to the file.
+ * @vector: array of vectors, pointing to the data to write in the file
+ * @count: number of items in the vector array
+ *
  *
  * Returns handle to update option if needed.
  *  Just the content can be updated, with smaller or equal to
  *  content than the specified size.
  */
 struct tracecmd_option *
-tracecmd_add_option(struct tracecmd_output *handle,
-		    unsigned short id, int size, const void *data)
+tracecmd_add_option_v(struct tracecmd_output *handle,
+		      unsigned short id, const struct iovec *vector, int count)
+
 {
 	struct tracecmd_option *option;
+	char *data = NULL;
+	int i, size = 0;
 
 	/*
 	 * We can only add options before they were written.
@@ -973,32 +979,63 @@ tracecmd_add_option(struct tracecmd_output *handle,
 	if (handle->options_written)
 		return NULL;
 
-	handle->nr_options++;
+	for (i = 0; i < count; i++)
+		size += vector[i].iov_len;
+	/* Some IDs (like TRACECMD_OPTION_TRACECLOCK) pass vector with 0 / NULL data */
+	if (size) {
+		data = malloc(size);
+		if (!data) {
+			warning("Insufficient memory");
+			return NULL;
+		}
+	}
 
 	option = malloc(sizeof(*option));
 	if (!option) {
 		warning("Could not allocate space for option");
+		free(data);
 		return NULL;
 	}
 
-	option->id = id;
-	option->size = size;
-	option->data = malloc(size);
-	if (!option->data) {
-		warning("Insufficient memory");
-		free(option);
-		return NULL;
+	handle->nr_options++;
+	option->data = data;
+	for (i = 0; i < count; i++) {
+		if (vector[i].iov_base && vector[i].iov_len) {
+			memcpy(data, vector[i].iov_base, vector[i].iov_len);
+			data += vector[i].iov_len;
+		}
 	}
 
-	/* Some IDs (like TRACECMD_OPTION_TRACECLOCK) pass 0 / NULL data */
-	if (size)
-		memcpy(option->data, data, size);
+	option->size = size;
+	option->id = id;
 
 	list_add_tail(&option->list, &handle->options);
 
 	return option;
 }
 
+/**
+ * tracecmd_add_option - add options to the file
+ * @handle: the output file handle name
+ * @id: the id of the option
+ * @size: the size of the option data
+ * @data: the data to write to the file.
+ *
+ * Returns handle to update option if needed.
+ *  Just the content can be updated, with smaller or equal to
+ *  content than the specified size.
+ */
+struct tracecmd_option *
+tracecmd_add_option(struct tracecmd_output *handle,
+		    unsigned short id, int size, const void *data)
+{
+	struct iovec vect;
+
+	vect.iov_base = (void *) data;
+	vect.iov_len = size;
+	return tracecmd_add_option_v(handle, id, &vect, 1);
+}
+
 int tracecmd_write_cpus(struct tracecmd_output *handle, int cpus)
 {
 	cpus = convert_endian_4(handle, cpus);
@@ -1046,6 +1083,57 @@ int tracecmd_write_options(struct tracecmd_output *handle)
 	return 0;
 }
 
+int tracecmd_append_options(struct tracecmd_output *handle)
+{
+	struct tracecmd_option *options;
+	unsigned short option;
+	unsigned short endian2;
+	unsigned int endian4;
+	off_t offset;
+	int r;
+
+	/* If already written, ignore */
+	if (handle->options_written)
+		return 0;
+
+	if (lseek64(handle->fd, 0, SEEK_END) == (off_t)-1)
+		return -1;
+	offset = lseek64(handle->fd, -2, SEEK_CUR);
+	if (offset == (off_t)-1)
+		return -1;
+
+	r = pread(handle->fd, &option, 2, offset);
+	if (r != 2 || option != TRACECMD_OPTION_DONE)
+		return -1;
+
+	list_for_each_entry(options, &handle->options, list) {
+		endian2 = convert_endian_2(handle, options->id);
+		if (do_write_check(handle, &endian2, 2))
+			return -1;
+
+		endian4 = convert_endian_4(handle, options->size);
+		if (do_write_check(handle, &endian4, 4))
+			return -1;
+
+		/* Save the data location in case it needs to be updated */
+		options->offset = lseek64(handle->fd, 0, SEEK_CUR);
+
+		if (do_write_check(handle, options->data,
+				   options->size))
+			return -1;
+	}
+
+	option = TRACECMD_OPTION_DONE;
+
+	if (do_write_check(handle, &option, 2))
+		return -1;
+
+	handle->options_written = 1;
+	handle->options_written = 1;
+
+	return 0;
+}
+
 int tracecmd_update_option(struct tracecmd_output *handle,
 			   struct tracecmd_option *option, int size,
 			   const void *data)
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 09/18] trace-cmd: Add new API to generate a unique ID of the tracing session
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (7 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 08/18] trace-cmd: Implement new API tracecmd_add_option_v() Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 10/18] trace-cmd: Store the session tracing ID in the trace.dat file Tzvetomir Stoyanov (VMware)
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

A new libtracecmd API is implemented:
	unsigned long long tracecmd_generate_traceid(void);
At trace-cmd init phase a unique ID, used for the current tracing session.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h  |  1 +
 lib/trace-cmd/trace-util.c     | 31 +++++++++++++++++++++++++++++++
 tracecmd/include/trace-local.h |  1 +
 tracecmd/trace-record.c        |  1 +
 4 files changed, 34 insertions(+)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 47fa463..6d50b08 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -441,6 +441,7 @@ void tracecmd_plog_error(const char *fmt, ...);
 int tracecmd_set_logfile(char *logfile);
 
 /* --- System --- */
+unsigned long long tracecmd_generate_traceid(void);
 int tracecmd_count_cpus(void);
 
 /* --- Hack! --- */
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index a54a905..2231eb8 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -18,6 +18,8 @@
 #include <sys/mount.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/sysinfo.h>
+#include <time.h>
 
 #include "trace-cmd.h"
 #include "event-utils.h"
@@ -1299,3 +1301,32 @@ error:
 	tracecmd_put_tracing_file(path);
 	return ret;
 }
+
+#define FNV_64_PRIME 0x100000001b3ULL
+unsigned long long tracecmd_generate_traceid(void)
+{
+	unsigned long long hash = 0;
+	unsigned char *ustr;
+	struct sysinfo sinfo;
+	struct timespec ts;
+	char *str = NULL;
+
+	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+	sysinfo(&sinfo);
+	asprintf(&str, "%ld %ld %ld %ld %ld %ld %ld %ld %d",
+		 ts.tv_sec, ts.tv_nsec,
+		 sinfo.loads[0], sinfo.loads[1], sinfo.loads[2],
+		 sinfo.freeram, sinfo.sharedram, sinfo.freeswap,
+		 sinfo.procs);
+	if (!str)
+		return 0;
+	ustr = (unsigned char *)str;
+	hash = 0;
+	while (*ustr) {
+		hash ^= (unsigned long long)*ustr++;
+		hash *= FNV_64_PRIME;
+	}
+
+	free(str);
+	return hash;
+}
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index 51abef1..6203e0d 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -182,6 +182,7 @@ struct pid_addr_maps {
 struct buffer_instance {
 	struct buffer_instance	*next;
 	struct tracecmd_instance	*ftrace;
+	unsigned long long	trace_id;
 	char			*cpumask;
 	struct event_list	*events;
 	struct event_list	**event_next;
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index fd7ca82..1583c53 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -5062,6 +5062,7 @@ void init_top_instance(void)
 		top_instance.ftrace = tracecmd_create_instance(NULL);
 	top_instance.cpu_count = tracecmd_count_cpus();
 	top_instance.flags = BUFFER_FL_KEEP;
+	top_instance.trace_id = tracecmd_generate_traceid();
 	tracecmd_init_instance(&top_instance);
 }
 
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 10/18] trace-cmd: Store the session tracing ID in the trace.dat file
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (8 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 09/18] trace-cmd: Add new API to generate a unique ID of the tracing session Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 11/18] trace-cmd: Exchange tracing IDs between host and guest Tzvetomir Stoyanov (VMware)
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

The ID of the current tracing session is stored in the trace.dat file.
A new file option is introduced:
	TRACECMD_OPTION_TRACEID
The data is stored in the file as NULL terminated string:
	"\nTraceID: %llu\n"
A new libtracecmd API is introduced, to get the ID when the trace.dat file is read:
	unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle);

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h |  2 ++
 lib/trace-cmd/trace-input.c   | 25 +++++++++++++++++++++++++
 tracecmd/trace-record.c       | 15 +++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 6d50b08..82def44 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -86,6 +86,7 @@ enum {
 	TRACECMD_OPTION_CPUCOUNT,
 	TRACECMD_OPTION_VERSION,
 	TRACECMD_OPTION_PROCMAPS,
+	TRACECMD_OPTION_TRACEID,
 };
 
 enum {
@@ -128,6 +129,7 @@ int tracecmd_copy_headers(struct tracecmd_input *handle, int fd);
 void tracecmd_set_flag(struct tracecmd_input *handle, int flag);
 void tracecmd_clear_flag(struct tracecmd_input *handle, int flag);
 unsigned long tracecmd_get_flags(struct tracecmd_input *handle);
+unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle);
 
 void tracecmd_parse_trace_clock(struct tracecmd_input *handle, char *file, int size);
 
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index 91362f3..40d9825 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -79,6 +79,7 @@ struct tracecmd_input {
 	struct tep_plugin_list	*plugin_list;
 	struct tracecmd_input	*parent;
 	unsigned long		flags;
+	unsigned long long	trace_id;
 	int			fd;
 	int			long_size;
 	int			page_size;
@@ -2169,6 +2170,15 @@ static void procmap_free(struct pid_addr_maps *maps)
 	free(maps);
 }
 
+static int trace_traceid_load(struct tracecmd_input *handle, char *buf)
+{
+
+	if (sscanf(buf, "%*s %llu", &handle->trace_id) == 1)
+		return 0;
+
+	return -1;
+}
+
 #define STR_PROCMAP_LINE_MAX	(PATH_MAX+22)
 static int trace_pid_map_load(struct tracecmd_input *handle, char *buf)
 {
@@ -2406,6 +2416,10 @@ static int handle_options(struct tracecmd_input *handle)
 			if (buf[size-1] == '\0')
 				trace_pid_map_load(handle, buf);
 			break;
+		case TRACECMD_OPTION_TRACEID:
+			if (buf[size-1] == '\0')
+				trace_traceid_load(handle, buf);
+			break;
 		default:
 			warning("unknown option %d", option);
 			break;
@@ -3507,3 +3521,14 @@ void tracecmd_set_show_data_func(struct tracecmd_input *handle,
 {
 	handle->show_data_func = func;
 }
+
+/**
+ * tracecmd_get_traceid - get the trace id of the session
+ * @handle: input handle for the trace.dat file
+ *
+ * Returns the trace id, written in the trace file
+ */
+unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
+{
+	return handle->trace_id;
+}
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 1583c53..01a74f1 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -3715,6 +3715,19 @@ add_pid_maps(struct tracecmd_output *handle, struct buffer_instance *instance)
 	trace_seq_destroy(&s);
 }
 
+static void
+add_trace_id(struct tracecmd_output *handle, struct buffer_instance *instance)
+{
+	char *str = NULL;
+	int len;
+
+	len = asprintf(&str, "\nTraceID: %llu\n", instance->trace_id);
+	if (len < 1)
+		return;
+	tracecmd_add_option(handle, TRACECMD_OPTION_TRACEID, len + 1, str);
+	free(str);
+}
+
 static void
 add_buffer_stat(struct tracecmd_output *handle, struct buffer_instance *instance)
 {
@@ -3818,6 +3831,8 @@ static void add_options(struct tracecmd_output *handle, struct common_record_con
 	add_option_hooks(handle);
 	add_uname(handle);
 	add_version(handle);
+	if (!no_top_instance())
+		add_trace_id(handle, &top_instance);
 }
 
 static void write_guest_file(struct buffer_instance *instance)
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 11/18] trace-cmd: Exchange tracing IDs between host and guest
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (9 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 10/18] trace-cmd: Store the session tracing ID in the trace.dat file Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-04 22:03   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov (VMware)
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Extend the trace request and trace reply messages, to include also
the IDs of host and guest tracing sessions. Those IDs are used to
unambiguously match the tracing sessions, when reading trace.dat files.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h  | 12 ++++++++----
 lib/trace-cmd/trace-msg.c      | 31 +++++++++++++++++++++++--------
 tracecmd/include/trace-local.h |  5 +++--
 tracecmd/trace-agent.c         | 12 +++++++-----
 tracecmd/trace-record.c        | 12 +++++++++---
 5 files changed, 50 insertions(+), 22 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 82def44..90fa146 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -353,16 +353,20 @@ bool tracecmd_msg_done(struct tracecmd_msg_handle *msg_handle);
 void tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle);
 
 int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
-				int argc, char **argv, bool use_fifos);
+				int argc, char **argv, bool use_fifos,
+				unsigned long long trace_id);
 int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
-				int *argc, char ***argv, bool *use_fifos);
+				int *argc, char ***argv, bool *use_fifos,
+				unsigned long long *trace_id);
 
 int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int nr_cpus, int page_size,
-				 unsigned int *ports, bool use_fifos);
+				 unsigned int *ports, bool use_fifos,
+				 unsigned long long trace_id);
 int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int *nr_cpus, int *page_size,
-				 unsigned int **ports, bool *use_fifos);
+				 unsigned int **ports, bool *use_fifos,
+				 unsigned long long *trace_id);
 /* --- ftrace instances --- */
 
 struct tracecmd_instance {
diff --git a/lib/trace-cmd/trace-msg.c b/lib/trace-cmd/trace-msg.c
index 9c8a690..a34297f 100644
--- a/lib/trace-cmd/trace-msg.c
+++ b/lib/trace-cmd/trace-msg.c
@@ -63,12 +63,14 @@ struct tracecmd_msg_rinit {
 struct tracecmd_msg_trace_req {
 	be32 flags;
 	be32 argc;
+	u64 trace_id;
 } __attribute__((packed));
 
 struct tracecmd_msg_trace_resp {
 	be32 flags;
 	be32 cpus;
 	be32 page_size;
+	u64 trace_id;
 } __attribute__((packed));
 
 struct tracecmd_msg_header {
@@ -811,7 +813,8 @@ int tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle)
 	return tracecmd_msg_wait_for_cmd(msg_handle, MSG_CLOSE_RESP);
 }
 
-static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv, bool use_fifos)
+static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
+			  bool use_fifos, unsigned long long trace_id)
 {
 	size_t args_size = 0;
 	char *p;
@@ -823,6 +826,7 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv, bool
 	msg->hdr.size = htonl(ntohl(msg->hdr.size) + args_size);
 	msg->trace_req.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
 	msg->trace_req.argc = htonl(argc);
+	msg->trace_req.trace_id = htonll(trace_id);
 	msg->buf = calloc(args_size, 1);
 	if (!msg->buf)
 		return -ENOMEM;
@@ -835,13 +839,15 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv, bool
 }
 
 int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
-				int argc, char **argv, bool use_fifos)
+				int argc, char **argv, bool use_fifos,
+				unsigned long long trace_id)
 {
 	struct tracecmd_msg msg;
 	int ret;
 
 	tracecmd_msg_init(MSG_TRACE_REQ, &msg);
-	ret = make_trace_req(&msg, argc, argv, use_fifos);
+	ret = make_trace_req(&msg, argc, argv,
+			     use_fifos, trace_id);
 	if (ret < 0)
 		return ret;
 
@@ -854,7 +860,8 @@ int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
   *     free(argv);
   */
 int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
-				int *argc, char ***argv, bool *use_fifos)
+				int *argc, char ***argv, bool *use_fifos,
+				unsigned long long *trace_id)
 {
 	struct tracecmd_msg msg;
 	char *p, *buf_end, **args;
@@ -901,6 +908,7 @@ int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
 	*argc = nr_args;
 	*argv = args;
 	*use_fifos = ntohl(msg.trace_req.flags) & MSG_TRACE_USE_FIFOS;
+	*trace_id = ntohll(msg.trace_req.trace_id);
 
 	/*
 	 * On success we're passing msg.buf to the caller through argv[0] so we
@@ -921,7 +929,8 @@ out:
 }
 
 static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
-			   unsigned int *ports, bool use_fifos)
+			   unsigned int *ports, bool use_fifos,
+			   unsigned long long trace_id)
 {
 	int data_size;
 
@@ -935,19 +944,22 @@ static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
 	msg->trace_resp.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
 	msg->trace_resp.cpus = htonl(nr_cpus);
 	msg->trace_resp.page_size = htonl(page_size);
+	msg->trace_resp.trace_id = htonll(trace_id);
 
 	return 0;
 }
 
 int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int nr_cpus, int page_size,
-				 unsigned int *ports, bool use_fifos)
+				 unsigned int *ports, bool use_fifos,
+				 unsigned long long trace_id)
 {
 	struct tracecmd_msg msg;
 	int ret;
 
 	tracecmd_msg_init(MSG_TRACE_RESP, &msg);
-	ret = make_trace_resp(&msg, page_size, nr_cpus, ports, use_fifos);
+	ret = make_trace_resp(&msg, page_size, nr_cpus, ports,
+			      use_fifos, trace_id);
 	if (ret < 0)
 		return ret;
 
@@ -956,13 +968,15 @@ int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 
 int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int *nr_cpus, int *page_size,
-				 unsigned int **ports, bool *use_fifos)
+				 unsigned int **ports, bool *use_fifos,
+				 unsigned long long *trace_id)
 {
 	struct tracecmd_msg msg;
 	char *p, *buf_end;
 	ssize_t buf_len;
 	int i, ret;
 
+	memset(&msg, 0, sizeof(msg));
 	ret = tracecmd_msg_recv(msg_handle->fd, &msg);
 	if (ret < 0)
 		return ret;
@@ -981,6 +995,7 @@ int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 	*use_fifos = ntohl(msg.trace_resp.flags) & MSG_TRACE_USE_FIFOS;
 	*nr_cpus = ntohl(msg.trace_resp.cpus);
 	*page_size = ntohl(msg.trace_resp.page_size);
+	*trace_id = ntohll(msg.trace_resp.trace_id);
 	*ports = calloc(*nr_cpus, sizeof(**ports));
 	if (!*ports) {
 		ret = -ENOMEM;
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index 6203e0d..a6f79c5 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -98,8 +98,9 @@ void trace_usage(int argc, char **argv);
 
 int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
 		       int cpus, int *fds,
-		       int argc, char **argv, bool use_fifos);
-
+		       int argc, char **argv,
+		       bool use_fifos,
+		       unsigned long long trace_id);
 struct hook_list;
 
 void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hooks,
diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
index 1c6e0a3..cc330b7 100644
--- a/tracecmd/trace-agent.c
+++ b/tracecmd/trace-agent.c
@@ -128,6 +128,7 @@ cleanup:
 static void agent_handle(int sd, int nr_cpus, int page_size)
 {
 	struct tracecmd_msg_handle *msg_handle;
+	unsigned long long trace_id;
 	unsigned int *ports;
 	char **argv = NULL;
 	int argc = 0;
@@ -144,7 +145,8 @@ static void agent_handle(int sd, int nr_cpus, int page_size)
 	if (!msg_handle)
 		die("Failed to allocate message handle");
 
-	ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv, &use_fifos);
+	ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
+					  &use_fifos, &trace_id);
 	if (ret < 0)
 		die("Failed to receive trace request");
 
@@ -153,13 +155,13 @@ static void agent_handle(int sd, int nr_cpus, int page_size)
 
 	if (!use_fifos)
 		make_vsocks(nr_cpus, fds, ports);
-
+	trace_id = tracecmd_generate_traceid();
 	ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
-					   ports, use_fifos);
+					   ports, use_fifos, trace_id);
 	if (ret < 0)
 		die("Failed to send trace response");
-
-	trace_record_agent(msg_handle, nr_cpus, fds, argc, argv, use_fifos);
+	trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
+			   use_fifos, trace_id);
 
 	free(argv[0]);
 	free(argv);
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 01a74f1..e51134f 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -3507,12 +3507,16 @@ static void connect_to_agent(struct buffer_instance *instance)
 		die("Failed to allocate message handle");
 
 	ret = tracecmd_msg_send_trace_req(msg_handle, instance->argc,
-					  instance->argv, use_fifos);
+					  instance->argv, use_fifos,
+					  top_instance.trace_id);
+
 	if (ret < 0)
 		die("Failed to send trace request");
 
 	ret = tracecmd_msg_recv_trace_resp(msg_handle, &nr_cpus, &page_size,
-					   &ports, &use_fifos);
+					   &ports, &use_fifos,
+					   &instance->trace_id);
+
 	if (ret < 0)
 		die("Failed to receive trace response");
 
@@ -6117,7 +6121,8 @@ void trace_record(int argc, char **argv)
 int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
 		       int cpus, int *fds,
 		       int argc, char **argv,
-		       bool use_fifos)
+		       bool use_fifos,
+		       unsigned long long trace_id)
 {
 	struct common_record_context ctx;
 	char **argv_plus;
@@ -6147,6 +6152,7 @@ int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
 	ctx.instance->flags |= BUFFER_FL_AGENT;
 	ctx.instance->msg_handle = msg_handle;
 	msg_handle->version = V3_PROTOCOL;
+	top_instance.trace_id = trace_id;
 	record_trace(argc, argv, &ctx);
 
 	free(argv_plus);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (10 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 11/18] trace-cmd: Exchange tracing IDs between host and guest Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-05  0:46   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 13/18] trace-cmd: Add guest information in host's trace.dat file Tzvetomir Stoyanov (VMware)
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

From: Tzvetomir Stoyanov <tstoyanov@vmware.com>

The TRACECMD_OPTION_TIME_SHIFT is used when synchronizing trace time stamps between
two trace.dat files. It contains multiple long long (time, offset) pairs, describing
time stamps _offset_, measured in the given local _time_. The content of the option
buffer is:
 8 bytes - long long integer, ID of the tracing session
 4 bytes - integer, count of timestamp offsets
 long long array of size _count_, local time in which the offset is measured
 long long array of size _count_, offset of the time stamps

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 include/trace-cmd/trace-cmd.h |   6 ++
 lib/trace-cmd/trace-input.c   | 170 +++++++++++++++++++++++++++++++++-
 2 files changed, 174 insertions(+), 2 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 90fa146..1240b4a 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -87,6 +87,7 @@ enum {
 	TRACECMD_OPTION_VERSION,
 	TRACECMD_OPTION_PROCMAPS,
 	TRACECMD_OPTION_TRACEID,
+	TRACECMD_OPTION_TIME_SHIFT,
 };
 
 enum {
@@ -130,6 +131,11 @@ void tracecmd_set_flag(struct tracecmd_input *handle, int flag);
 void tracecmd_clear_flag(struct tracecmd_input *handle, int flag);
 unsigned long tracecmd_get_flags(struct tracecmd_input *handle);
 unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle);
+int tracecmd_get_guest_cpumap(struct tracecmd_input *handle,
+			      unsigned long long trace_id,
+			      char *name,
+			      int *vcpu_count, int *cpu_pid);
+unsigned long long tracecmd_get_tsync_peer(struct tracecmd_input *handle);
 
 void tracecmd_parse_trace_clock(struct tracecmd_input *handle, char *file, int size);
 
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index 40d9825..a6b675a 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -74,6 +74,18 @@ struct input_buffer_instance {
 	size_t			offset;
 };
 
+struct ts_offset_sample {
+	long long	time;
+	long long	offset;
+};
+
+struct host_trace_info {
+	bool			sync_enable;
+	unsigned long long	trace_id;
+	int			ts_samples_count;
+	struct ts_offset_sample	*ts_samples;
+};
+
 struct tracecmd_input {
 	struct tep_handle	*pevent;
 	struct tep_plugin_list	*plugin_list;
@@ -92,6 +104,7 @@ struct tracecmd_input {
 	bool			use_pipe;
 	struct cpu_data 	*cpu_data;
 	long long		ts_offset;
+	struct host_trace_info	host;
 	double			ts2secs;
 	char *			cpustats;
 	char *			uname;
@@ -1072,6 +1085,67 @@ static void free_next(struct tracecmd_input *handle, int cpu)
 	free_record(record);
 }
 
+static inline unsigned long long
+timestamp_correction_calc(unsigned long long ts, struct ts_offset_sample *min,
+			  struct ts_offset_sample *max)
+{
+	long long tscor = min->offset +
+			(((((long long)ts) - min->time)*
+			(max->offset-min->offset))/(max->time-min->time));
+
+	if (tscor < 0)
+		return ts - llabs(tscor);
+
+	return ts + tscor;
+}
+
+static unsigned long long timestamp_correct(unsigned long long ts,
+					    struct tracecmd_input *handle)
+{
+	struct host_trace_info	*host = &handle->host;
+	int min, mid, max;
+
+	if (handle->ts_offset)
+		return ts + handle->ts_offset;
+
+	if (!host->sync_enable || !host->ts_samples_count || !host->ts_samples)
+		return ts;
+
+	/* We have one sample, nothing to calc here */
+	if (host->ts_samples_count == 1)
+		return ts + host->ts_samples[0].offset;
+
+	/* We have two samples, nothing to search here */
+	if (host->ts_samples_count == 2)
+		return timestamp_correction_calc(ts, &host->ts_samples[0],
+						 &host->ts_samples[1]);
+
+	/* We have more than two samples */
+	if (ts <= host->ts_samples[0].time)
+		return timestamp_correction_calc(ts,
+						 &host->ts_samples[0],
+						 &host->ts_samples[1]);
+	else if (ts >= host->ts_samples[host->ts_samples_count-1].time)
+		return timestamp_correction_calc(ts,
+						 &host->ts_samples[host->ts_samples_count-2],
+						 &host->ts_samples[host->ts_samples_count-1]);
+	min = 0;
+	max = host->ts_samples_count-1;
+	mid = (min + max)/2;
+	while (min <= max) {
+		if (ts < host->ts_samples[mid].time)
+			max = mid - 1;
+		else if (ts > host->ts_samples[mid].time)
+			min = mid + 1;
+		else
+			break;
+		mid = (min + max)/2;
+	}
+
+	return timestamp_correction_calc(ts, &host->ts_samples[mid],
+					 &host->ts_samples[mid+1]);
+}
+
 /*
  * Page is mapped, now read in the page header info.
  */
@@ -1093,7 +1167,7 @@ static int update_page_info(struct tracecmd_input *handle, int cpu)
 		    kbuffer_subbuffer_size(kbuf));
 		return -1;
 	}
-	handle->cpu_data[cpu].timestamp = kbuffer_timestamp(kbuf) + handle->ts_offset;
+	handle->cpu_data[cpu].timestamp = timestamp_correct(kbuffer_timestamp(kbuf), handle);
 
 	if (handle->ts2secs)
 		handle->cpu_data[cpu].timestamp *= handle->ts2secs;
@@ -1820,7 +1894,7 @@ read_again:
 		goto read_again;
 	}
 
-	handle->cpu_data[cpu].timestamp = ts + handle->ts_offset;
+	handle->cpu_data[cpu].timestamp = timestamp_correct(ts, handle);
 
 	if (handle->ts2secs) {
 		handle->cpu_data[cpu].timestamp *= handle->ts2secs;
@@ -2143,6 +2217,47 @@ void tracecmd_set_ts2secs(struct tracecmd_input *handle,
 	handle->use_trace_clock = false;
 }
 
+static int tsync_offset_cmp(const void *a, const void *b)
+{
+	struct ts_offset_sample *ts_a = (struct ts_offset_sample *)a;
+	struct ts_offset_sample *ts_b = (struct ts_offset_sample *)b;
+
+	if (ts_a->time > ts_b->time)
+		return 1;
+	if (ts_a->time < ts_b->time)
+		return -1;
+	return 0;
+}
+
+static void tsync_offset_load(struct tracecmd_input *handle, char *buf)
+{
+	struct host_trace_info *host = &handle->host;
+	long long *buf8 = (long long *)buf;
+	int i, j;
+
+	for (i = 0; i < host->ts_samples_count; i++) {
+		host->ts_samples[i].time = tep_read_number(handle->pevent,
+							   buf8 + i, 8);
+		host->ts_samples[i].offset = tep_read_number(handle->pevent,
+						buf8 + host->ts_samples_count+i, 8);
+	}
+	qsort(host->ts_samples, host->ts_samples_count,
+	      sizeof(struct ts_offset_sample), tsync_offset_cmp);
+	/* Filter possible samples with equal time */
+	for (i = 0, j = 0; i < host->ts_samples_count; i++) {
+		if (i == 0 || host->ts_samples[i].time != host->ts_samples[i-1].time)
+			host->ts_samples[j++] = host->ts_samples[i];
+	}
+	host->ts_samples_count = j;
+	host->sync_enable = true;
+}
+
+static void trace_tsync_offset_free(struct host_trace_info *host)
+{
+	free(host->ts_samples);
+	host->ts_samples = NULL;
+}
+
 static int trace_pid_map_cmp(const void *a, const void *b)
 {
 	struct tracecmd_proc_addr_map *m_a = (struct tracecmd_proc_addr_map *)a;
@@ -2323,6 +2438,7 @@ static int handle_options(struct tracecmd_input *handle)
 	struct input_buffer_instance *buffer;
 	struct hook_list *hook;
 	char *buf;
+	int sampes_size;
 	int cpus;
 
 	/* By default, use usecs, unless told otherwise */
@@ -2370,6 +2486,28 @@ static int handle_options(struct tracecmd_input *handle)
 			offset = strtoll(buf, NULL, 0);
 			handle->ts_offset += offset;
 			break;
+		case TRACECMD_OPTION_TIME_SHIFT:
+			/*
+			 * long long int (8 bytes) trace session ID
+			 * int (4 bytes) count of timestamp offsets.
+			 * long long array of size [count] of times,
+			 *      when the offsets were calculated.
+			 * long long array of size [count] of timestamp offsets.
+			 */
+			if (size < 12 || handle->flags & TRACECMD_FL_IGNORE_DATE)
+				break;
+			handle->host.trace_id = tep_read_number(handle->pevent,
+								buf, 8);
+			handle->host.ts_samples_count = tep_read_number(handle->pevent,
+									buf + 8, 4);
+			sampes_size = (8 * handle->host.ts_samples_count);
+			if (size != (12 + (2 * sampes_size)))
+				break;
+			handle->host.ts_samples = malloc(2 * sampes_size);
+			if (!handle->host.ts_samples)
+				return -ENOMEM;
+			tsync_offset_load(handle, buf + 12);
+			break;
 		case TRACECMD_OPTION_CPUSTAT:
 			buf[size-1] = '\n';
 			cpustats = realloc(cpustats, cpustats_size + size + 1);
@@ -3078,6 +3216,8 @@ void tracecmd_close(struct tracecmd_input *handle)
 	trace_pid_map_free(handle->pid_maps);
 	handle->pid_maps = NULL;
 
+	trace_tsync_offset_free(&handle->host);
+
 	if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
 		tracecmd_close(handle->parent);
 	else {
@@ -3532,3 +3672,29 @@ unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
 {
 	return handle->trace_id;
 }
+
+/**
+ * tracecmd_get_tsync_peer - get the trace session id of the peer host
+ * @handle: input handle for the trace.dat file
+ *
+ * Returns the trace id of the peer host, written in the trace file
+ *
+ * This information is stored in guest trace.dat file
+ */
+unsigned long long tracecmd_get_tsync_peer(struct tracecmd_input *handle)
+{
+	return handle->host.trace_id;
+}
+
+/**
+ * tracecmd_enable_tsync - enable / disable the timestamps correction
+ * @handle: input handle for the trace.dat file
+ * @enable: enable / disable the timestamps correction
+ *
+ * Enables or disables timestamps correction on file load, using the array of
+ * recorded time offsets
+ */
+void tracecmd_enable_tsync(struct tracecmd_input *handle, bool enable)
+{
+	handle->host.sync_enable = enable;
+}
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 13/18] trace-cmd: Add guest information in host's trace.dat file
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (11 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-05  0:59   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument Tzvetomir Stoyanov (VMware)
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

New trace.dat option is introduced: TRACECMD_OPTION_GUEST.
Written in the host's trace.dat file, it contains information about
guests, traced at the same time: guest trace ID, number of VCPUs and
PIDs of the host tasks, running those VCPU. The data is stored in
the file as NULL terminated string:
	"Guest %s %llu %d\n" -> guest name, number of VCPUs
	"%d %d\n" -> VCPU, PID of host task
	.....
	"%d %d\n" -> VCPU, PID of host task

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h |   1 +
 lib/trace-cmd/trace-input.c   | 113 ++++++++++++++++++++++++++++++++++
 tracecmd/trace-record.c       |  45 ++++++++++++++
 3 files changed, 159 insertions(+)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 1240b4a..17badf2 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -88,6 +88,7 @@ enum {
 	TRACECMD_OPTION_PROCMAPS,
 	TRACECMD_OPTION_TRACEID,
 	TRACECMD_OPTION_TIME_SHIFT,
+	TRACECMD_OPTION_GUEST,
 };
 
 enum {
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index a6b675a..e36ebaa 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -79,6 +79,14 @@ struct ts_offset_sample {
 	long long	offset;
 };
 
+struct guest_trace_info {
+	struct guest_trace_info	*next;
+	char			*name;
+	unsigned long long	trace_id;
+	int			vcpu_count;
+	int			*cpu_pid;
+};
+
 struct host_trace_info {
 	bool			sync_enable;
 	unsigned long long	trace_id;
@@ -112,6 +120,7 @@ struct tracecmd_input {
 	char *			trace_clock;
 	struct input_buffer_instance	*buffers;
 	int			parsing_failures;
+	struct guest_trace_info	*guest;
 
 	struct tracecmd_ftrace	finfo;
 
@@ -2294,6 +2303,73 @@ static int trace_traceid_load(struct tracecmd_input *handle, char *buf)
 	return -1;
 }
 
+static void trace_guests_free(struct tracecmd_input *handle)
+{
+	struct guest_trace_info *guest;
+
+	while (handle->guest) {
+		guest = handle->guest;
+		handle->guest = handle->guest->next;
+		free(guest->name);
+		free(guest->cpu_pid);
+		free(guest);
+	}
+}
+
+static int trace_guest_load(struct tracecmd_input *handle, char *buf)
+{
+	struct guest_trace_info *guest = NULL;
+	unsigned long long tid;
+	char *line;
+	int cpu, pid;
+
+	line = strchr(buf, '\n');
+	if (!line)
+		goto error;
+	*line = '\0';
+
+	guest = calloc(1, sizeof(struct guest_trace_info));
+	if (!guest)
+		goto error;
+
+	if (sscanf(buf, "%*s %ms %llu %d", &guest->name, &tid, &cpu) != 3)
+		goto error;
+	guest->trace_id = tid;
+	guest->vcpu_count = cpu;
+
+	guest->cpu_pid = calloc(guest->vcpu_count, sizeof(int));
+	if (!guest->cpu_pid)
+		goto error;
+
+	buf = line + 1;
+	line = strchr(buf, '\n');
+	while (line) {
+		*line = '\0';
+		if (sscanf(buf, "%d %d", &cpu, &pid) != 2)
+			goto error;
+
+		if (cpu < guest->vcpu_count)
+			guest->cpu_pid[cpu] = pid;
+		buf = line + 1;
+		if (!*buf)
+			break;
+
+		line = strchr(buf, '\n');
+	}
+
+	guest->next = handle->guest;
+	handle->guest = guest;
+	return 0;
+
+error:
+	if (guest) {
+		free(guest->cpu_pid);
+		free(guest->name);
+		free(guest);
+	}
+	return -1;
+}
+
 #define STR_PROCMAP_LINE_MAX	(PATH_MAX+22)
 static int trace_pid_map_load(struct tracecmd_input *handle, char *buf)
 {
@@ -2558,6 +2634,10 @@ static int handle_options(struct tracecmd_input *handle)
 			if (buf[size-1] == '\0')
 				trace_traceid_load(handle, buf);
 			break;
+		case TRACECMD_OPTION_GUEST:
+			if (buf[size-1] == '\0')
+				trace_guest_load(handle, buf);
+			break;
 		default:
 			warning("unknown option %d", option);
 			break;
@@ -3217,6 +3297,7 @@ void tracecmd_close(struct tracecmd_input *handle)
 	handle->pid_maps = NULL;
 
 	trace_tsync_offset_free(&handle->host);
+	trace_guests_free(handle);
 
 	if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
 		tracecmd_close(handle->parent);
@@ -3673,6 +3754,38 @@ unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
 	return handle->trace_id;
 }
 
+/**
+ * tracecmd_get_guest_cpumap - get the mapping of guest VCPU to host process
+ * @handle: input handle for the trace.dat file
+ * @trace_id: ID of the guest tracing session
+ *
+ * Returns @name of the guest, number of VPUs (@vcpu_count)
+ * and array @cpu_pid with size @vcpu_count. Array index is VCPU id, array
+ * content is PID of the host process, running this VCPU.
+ *
+ * This information is stored in host trace.dat file
+ */
+int tracecmd_get_guest_cpumap(struct tracecmd_input *handle,
+			      unsigned long long trace_id,
+			      char *name,
+			      int *vcpu_count, int *cpu_pid)
+{
+	struct guest_trace_info	*guest = handle->guest;
+
+	while (guest) {
+		if (guest->trace_id == trace_id)
+			break;
+		guest = guest->next;
+	}
+	if (!guest)
+		return -1;
+
+	name = guest->name;
+	*vcpu_count = guest->vcpu_count;
+	cpu_pid = guest->cpu_pid;
+	return 0;
+}
+
 /**
  * tracecmd_get_tsync_peer - get the trace session id of the peer host
  * @handle: input handle for the trace.dat file
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index e51134f..49730d6 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -2884,6 +2884,19 @@ struct guest {
 static struct guest *guests;
 static size_t guests_len;
 
+static struct guest *get_guest_info(unsigned int guest_cid)
+{
+	int i;
+
+	if (!guests)
+		return NULL;
+
+	for (i = 0; i < guests_len; i++)
+		if (guest_cid == guests[i].cid)
+			return guests + i;
+	return NULL;
+}
+
 static char *get_qemu_guest_name(char *arg)
 {
 	char *tok, *end = arg;
@@ -3689,6 +3702,33 @@ static void append_buffer(struct tracecmd_output *handle,
 	}
 }
 
+static void
+add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance)
+{
+	struct guest *guest = get_guest_info(instance->cid);
+	struct trace_seq s;
+	int i;
+
+	if (!guest)
+		return;
+	for (i = 0; i < VCPUS_MAX; i++)
+		if (!guest->cpu_pid[i])
+			break;
+
+	trace_seq_init(&s);
+
+	trace_seq_printf(&s, "Guest %s %llu %d\n",
+			 guest->name, instance->trace_id, i);
+	for (i = 0; i < VCPUS_MAX; i++) {
+		if (!guest->cpu_pid[i])
+			break;
+		trace_seq_printf(&s, "%d %d\n", i, guest->cpu_pid[i]);
+	}
+	trace_seq_terminate(&s);
+	tracecmd_add_option(handle, TRACECMD_OPTION_GUEST,
+			    s.len + 1, s.buffer);
+	trace_seq_destroy(&s);
+}
 
 static void
 add_pid_maps(struct tracecmd_output *handle, struct buffer_instance *instance)
@@ -3976,6 +4016,11 @@ static void record_data(struct common_record_context *ctx)
 			add_pid_maps(handle, instance);
 		}
 
+		for_all_instances(instance) {
+			if (is_guest(instance))
+				add_guest_info(handle, instance);
+		}
+
 		tracecmd_append_cpu_data(handle, local_cpu_count, temp_files);
 
 		for (i = 0; i < max_cpu_count; i++)
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (12 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 13/18] trace-cmd: Add guest information in host's trace.dat file Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-09 19:31   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 15/18] trace-cmd: Refactor few trace-cmd internal functions Tzvetomir Stoyanov (VMware)
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

When tracing host and guest machines, both should use the same
tracing clock for event timestamps. If a clock is specified
as host tracing argument, with option "-C clock_name", the same
is injected as guest tracing argument. If the user wants to use
different tracing clocks, it can specify it using "-C clock_name"
as guest tracing argument. In that case, the one specified by
the user has higher priority.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 tracecmd/trace-record.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 49730d6..e7fb1bd 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -5405,6 +5405,8 @@ static void parse_record_options(int argc,
 	char *sav;
 	int name_counter = 0;
 	int neg_event = 0;
+	struct buffer_instance *instance;
+	bool guest_config = false;
 
 	init_common_record_context(ctx, curr_cmd);
 
@@ -5562,6 +5564,7 @@ static void parse_record_options(int argc,
 			break;
 		case 'C':
 			ctx->instance->ftrace->clock = optarg;
+			guest_config = true;
 			break;
 		case 'v':
 			neg_event = 1;
@@ -5779,14 +5782,26 @@ static void parse_record_options(int argc,
 
 	/* If --date is specified, prepend it to all guest VM flags */
 	if (ctx->date) {
-		struct buffer_instance *instance;
-
 		for_all_instances(instance) {
 			if (is_guest(instance))
 				add_argv(instance, "--date", true);
 		}
 	}
 
+	if (guest_config) {
+		/* If -C is specified, prepend clock to all guest VM flags */
+		for_all_instances(instance) {
+			if (top_instance.ftrace->clock) {
+				if (is_guest(instance)) {
+					add_argv(instance,
+						 (char *)top_instance.ftrace->clock,
+						 true);
+					add_argv(instance, "-C", true);
+				}
+			}
+		}
+	}
+
 	if (!ctx->filtered && ctx->instance->filter_mod)
 		add_func(&ctx->instance->filter_funcs,
 			 ctx->instance->filter_mod, "*");
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 15/18] trace-cmd: Refactor few trace-cmd internal functions.
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (13 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-09 19:32   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization Tzvetomir Stoyanov (VMware)
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

In order to reuse code inside trace-cmd application context,
few functions are made non static:
	int make_vsock(unsigned int port);
	int get_vsock_port(int sd, unsigned int *port);
	int open_vsock(unsigned int cid, unsigned int port);
	char *get_guest_file(const char *file, const char *guest);

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 tracecmd/include/trace-local.h | 8 ++++++++
 tracecmd/trace-agent.c         | 4 ++--
 tracecmd/trace-record.c        | 8 ++++----
 3 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index a6f79c5..17ef31a 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -19,6 +19,8 @@
 #define GUEST_FIFO_FMT		GUEST_DIR_FMT "/" GUEST_PIPE_NAME "%d"
 #define VIRTIO_FIFO_FMT		"/dev/virtio-ports/" GUEST_PIPE_NAME "%d"
 
+#define TRACE_FILENAME		"trace.dat"
+
 /* fix stupid glib guint64 typecasts and printf formats */
 typedef unsigned long long u64;
 
@@ -258,6 +260,12 @@ void tracecmd_disable_tracing(void);
 void tracecmd_enable_tracing(void);
 void tracecmd_stat_cpu(struct trace_seq *s, int cpu);
 
+int make_vsock(unsigned int port);
+int get_vsock_port(int sd, unsigned int *port);
+int open_vsock(unsigned int cid, unsigned int port);
+
+char *get_guest_file(const char *file, const char *guest);
+
 /* No longer in event-utils.h */
 void __noreturn die(const char *fmt, ...); /* Can be overriden */
 void *malloc_or_die(unsigned int size); /* Can be overridden */
diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
index cc330b7..530f972 100644
--- a/tracecmd/trace-agent.c
+++ b/tracecmd/trace-agent.c
@@ -40,7 +40,7 @@ static int get_local_cid(unsigned int *cid)
 	return ret;
 }
 
-static int make_vsock(unsigned int port)
+int make_vsock(unsigned int port)
 {
 	struct sockaddr_vm addr = {
 		.svm_family = AF_VSOCK,
@@ -64,7 +64,7 @@ static int make_vsock(unsigned int port)
 	return sd;
 }
 
-static int get_vsock_port(int sd, unsigned int *port)
+int get_vsock_port(int sd, unsigned int *port)
 {
 	struct sockaddr_vm addr;
 	socklen_t addr_len = sizeof(addr);
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index e7fb1bd..e66f999 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -68,7 +68,7 @@ static int rt_prio;
 
 static int keep;
 
-static const char *output_file = "trace.dat";
+static const char *output_file = TRACE_FILENAME;
 
 static int latency;
 static int sleep_time = 1000;
@@ -536,7 +536,7 @@ static char *get_temp_file(struct buffer_instance *instance, int cpu)
 	return file;
 }
 
-static char *get_guest_file(const char *file, const char *guest)
+char *get_guest_file(const char *file, const char *guest)
 {
 	const char *p;
 	char *out = NULL;
@@ -2772,7 +2772,7 @@ static int connect_port(const char *host, unsigned int port)
 }
 
 #ifdef VSOCK
-static int open_vsock(unsigned int cid, unsigned int port)
+int open_vsock(unsigned int cid, unsigned int port)
 {
 	struct sockaddr_vm addr = {
 		.svm_family = AF_VSOCK,
@@ -2835,7 +2835,7 @@ static bool can_splice_read_vsock(void)
 }
 
 #else
-static inline int open_vsock(unsigned int cid, unsigned int port)
+int open_vsock(unsigned int cid, unsigned int port)
 {
 	die("vsock is not supported");
 	return -1;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (14 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 15/18] trace-cmd: Refactor few trace-cmd internal functions Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-10 17:04   ` Steven Rostedt
  2019-12-10 18:39   ` Steven Rostedt
  2019-12-03 10:35 ` [PATCH v17 17/18] trace-cmd: [POC] PTP-like algorithm " Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 18/18] trace-cmd: Debug scripts for " Tzvetomir Stoyanov (VMware)
  17 siblings, 2 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

The infrastructure for host - guest timestamp synchronization is divided
in two parts:
 - logic in libtracecmd
 - logic in trace-cmd application

The libtracecmd is extended with new trace message, MSG_TIME_SYNC, used to
exchange time synch information between host and guest. The trace request and
response messages are extended to handle the time synch negotiation. The logic
is implemented in trace-timesync.c file:
 - Register / unregister time sync protocols.
 - Choosing what protocol will be used for a tracing session.
 - Init common context, used by all protocols - a ftrace instance, vsockets.
 - Invoke protocol routines to perform time synchronization.
 - Store calculated offsets in an array.

The trace-cmd application is extended to perform timestamp synchronization.
The main logic is in trace-tsync.c file
 - Negotiate what time synch algorithm will be used for the tracing session.
 - Run pthreads, to do continuous timestamp synchronization, during the trace.
 - Store calculated offsets in guest's trace.dat file, using TRACECMD_OPTION_TIME_SHIFT option
 - A new trace-cmd option is added, to control the timestamp synchronization:
      --tsync-interval: set the time sync loop interval, in ms. The default
   value is 0 - timestamp synchronization is performed twice, before and after
   the trace. If a negative number is specified, the timestamp synchronization
   is disabled.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 Documentation/trace-cmd-record.1.txt      |   6 +
 include/trace-cmd/trace-cmd.h             |  54 ++-
 lib/trace-cmd/Makefile                    |   1 +
 lib/trace-cmd/include/trace-tsync-local.h |  36 ++
 lib/trace-cmd/trace-msg.c                 | 147 ++++++-
 lib/trace-cmd/trace-timesync.c            | 461 ++++++++++++++++++++++
 tracecmd/Makefile                         |   3 +-
 tracecmd/include/trace-local.h            |  11 +
 tracecmd/trace-agent.c                    |  34 +-
 tracecmd/trace-record.c                   |  37 +-
 tracecmd/trace-tsync.c                    | 268 +++++++++++++
 tracecmd/trace-usage.c                    |   4 +
 12 files changed, 1035 insertions(+), 27 deletions(-)
 create mode 100644 lib/trace-cmd/include/trace-tsync-local.h
 create mode 100644 lib/trace-cmd/trace-timesync.c
 create mode 100644 tracecmd/trace-tsync.c

diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt
index 0d75e43..6f09200 100644
--- a/Documentation/trace-cmd-record.1.txt
+++ b/Documentation/trace-cmd-record.1.txt
@@ -338,6 +338,12 @@ OPTIONS
     the offset will also be in nanoseconds even if the displayed units are
     in microseconds.
 
+*--tsync-interval*::
+    Set the loop interval, in ms, for timestamps synchronization with guests:
+        If a negative number is specified, timestamps synchronization is disabled
+        If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
+        at the beginning and at the end of the trace\n"
+
 *--stderr*::
     Have output go to stderr instead of stdout, but the output of the command
     executed will not be changed. This is useful if you want to monitor the
diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 17badf2..6abcc9f 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -6,6 +6,7 @@
 #ifndef _TRACE_CMD_H
 #define _TRACE_CMD_H
 
+#include <semaphore.h>
 #include "traceevent/event-parse.h"
 
 #define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))
@@ -361,19 +362,35 @@ void tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle);
 
 int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
 				int argc, char **argv, bool use_fifos,
-				unsigned long long trace_id);
+				unsigned long long trace_id,
+				unsigned int tsync_protos);
 int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
 				int *argc, char ***argv, bool *use_fifos,
-				unsigned long long *trace_id);
+				unsigned long long *trace_id,
+				unsigned int *tsync_protos);
 
 int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int nr_cpus, int page_size,
 				 unsigned int *ports, bool use_fifos,
-				 unsigned long long trace_id);
+				 unsigned long long trace_id,
+				 unsigned int tsync_proto,
+				 unsigned int tsync_port);
 int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int *nr_cpus, int *page_size,
 				 unsigned int **ports, bool *use_fifos,
-				 unsigned long long *trace_id);
+				 unsigned long long *trace_id,
+				 unsigned int *tsync_proto,
+				 unsigned int *tsync_port);
+
+int tracecmd_msg_send_time_sync(struct tracecmd_msg_handle *msg_handle,
+				unsigned int sync_protocol,
+				unsigned int sync_msg_id,
+				unsigned int payload_size, char *payload);
+int tracecmd_msg_recv_time_sync(struct tracecmd_msg_handle *msg_handle,
+				unsigned int *sync_protocol,
+				unsigned int *sync_msg_id,
+				unsigned int *payload_size, char **payload);
+
 /* --- ftrace instances --- */
 
 struct tracecmd_instance {
@@ -406,6 +423,35 @@ int tracecmd_iterate_raw_events(struct tep_handle *tep,
 				void *callback_context);
 
 
+/* --- Timestamp synchronization --- */
+
+enum{
+	TRACECMD_TIME_SYNC_PROTO_NONE	= 0,
+};
+enum{
+	TRACECMD_TIME_SYNC_CMD_PROBE	= 1,
+	TRACECMD_TIME_SYNC_CMD_STOP	= 2,
+};
+
+#define TRACECMD_TIME_SYNC_PROTO_PTP_WEIGHT	10
+
+struct tracecmd_time_sync {
+	unsigned int			sync_proto;
+	int				loop_interval;
+	sem_t				sem;
+	char				*clock_str;
+	struct tracecmd_msg_handle	*msg_handle;
+	void				*context;
+};
+
+unsigned int tracecmd_tsync_proto_getall(void);
+unsigned int tracecmd_tsync_proto_select(unsigned int protos);
+void tracecmd_tsync_with_host(struct tracecmd_time_sync *tsync);
+void tracecmd_tsync_with_guest(struct tracecmd_time_sync *tsync);
+int tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync,
+				int *count,
+				long long **ts, long long **offsets);
+void tracecmd_tsync_free(struct tracecmd_time_sync *tsync);
 
 /* --- Plugin handling --- */
 extern struct tep_plugin_option trace_ftrace_options[];
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 18c7013..6221b36 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -16,6 +16,7 @@ OBJS += trace-util.o
 OBJS += trace-instance.o
 OBJS += trace-filter-hash.o
 OBJS += trace-msg.o
+OBJS += trace-timesync.o
 
 # Additional util objects
 OBJS += trace-blk-hack.o
diff --git a/lib/trace-cmd/include/trace-tsync-local.h b/lib/trace-cmd/include/trace-tsync-local.h
new file mode 100644
index 0000000..72a6556
--- /dev/null
+++ b/lib/trace-cmd/include/trace-tsync-local.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#ifndef _TRACE_TSYNC_LOCAL_H
+#define _TRACE_TSYNC_LOCAL_H
+
+#include <stdbool.h>
+
+struct clock_sync_context {
+	void				*proto_data;
+	bool				server;
+	struct tracecmd_instance	*vinst;
+
+	int				sync_size;
+	int				sync_count;
+	long long			*sync_ts;
+	long long			*sync_offsets;
+
+	unsigned int			local_cid;
+	unsigned int			local_port;
+	unsigned int			remote_cid;
+	unsigned int			remote_port;
+};
+
+int tracecmd_tsync_proto_register(unsigned int proto_id, int weight,
+				int (*init)(struct tracecmd_time_sync *),
+				int (*free)(struct tracecmd_time_sync *),
+				int (*calc)(struct tracecmd_time_sync *,
+					    long long *, long long *));
+int tracecmd_tsync_proto_unregister(unsigned int proto_id);
+
+int ptp_clock_sync_register(void);
+
+#endif /* _TRACE_TSYNC_LOCAL_H */
diff --git a/lib/trace-cmd/trace-msg.c b/lib/trace-cmd/trace-msg.c
index a34297f..b6ea5fc 100644
--- a/lib/trace-cmd/trace-msg.c
+++ b/lib/trace-cmd/trace-msg.c
@@ -28,6 +28,8 @@
 
 typedef __u32 u32;
 typedef __be32 be32;
+typedef __u64 u64;
+typedef __s64 s64;
 
 static inline void dprint(const char *fmt, ...)
 {
@@ -64,6 +66,7 @@ struct tracecmd_msg_trace_req {
 	be32 flags;
 	be32 argc;
 	u64 trace_id;
+	be32 tsync_protos;
 } __attribute__((packed));
 
 struct tracecmd_msg_trace_resp {
@@ -71,6 +74,13 @@ struct tracecmd_msg_trace_resp {
 	be32 cpus;
 	be32 page_size;
 	u64 trace_id;
+	be32 tsync_proto;
+	be32 tsync_port;
+} __attribute__((packed));
+
+struct tracecmd_msg_tsync {
+	be32 sync_protocol;
+	be32 sync_msg_id;
 } __attribute__((packed));
 
 struct tracecmd_msg_header {
@@ -88,7 +98,8 @@ struct tracecmd_msg_header {
 	C(NOT_SUPP,	5,	0),					\
 	C(TRACE_REQ,	6,	sizeof(struct tracecmd_msg_trace_req)),	\
 	C(TRACE_RESP,	7,	sizeof(struct tracecmd_msg_trace_resp)),\
-	C(CLOSE_RESP,	8,	0),
+	C(CLOSE_RESP,	8,	0),					\
+	C(TIME_SYNC,	9,	sizeof(struct tracecmd_msg_tsync)),
 
 #undef C
 #define C(a,b,c)	MSG_##a = b
@@ -122,6 +133,7 @@ struct tracecmd_msg {
 		struct tracecmd_msg_rinit	rinit;
 		struct tracecmd_msg_trace_req	trace_req;
 		struct tracecmd_msg_trace_resp	trace_resp;
+		struct tracecmd_msg_tsync	tsync;
 	};
 	char					*buf;
 } __attribute__((packed));
@@ -264,6 +276,17 @@ static int tracecmd_msg_send(int fd, struct tracecmd_msg *msg)
 	return ret;
 }
 
+static int tracecmd_msg_send_nofree(int fd, struct tracecmd_msg *msg)
+{
+	int ret = 0;
+
+	ret = msg_write(fd, msg);
+	if (ret < 0)
+		ret = -ECOMM;
+
+	return ret;
+}
+
 static int msg_read(int fd, void *buf, u32 size, int *n)
 {
 	ssize_t r;
@@ -814,7 +837,8 @@ int tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle)
 }
 
 static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
-			  bool use_fifos, unsigned long long trace_id)
+			  bool use_fifos, unsigned long long trace_id,
+			  unsigned int tsync_protos)
 {
 	size_t args_size = 0;
 	char *p;
@@ -824,7 +848,11 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
 		args_size += strlen(argv[i]) + 1;
 
 	msg->hdr.size = htonl(ntohl(msg->hdr.size) + args_size);
-	msg->trace_req.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
+	msg->trace_req.flags = 0;
+	if (use_fifos)
+		msg->trace_req.flags |= MSG_TRACE_USE_FIFOS;
+	msg->trace_req.flags = htonl(msg->trace_req.flags);
+	msg->trace_req.tsync_protos = htonl(tsync_protos);
 	msg->trace_req.argc = htonl(argc);
 	msg->trace_req.trace_id = htonll(trace_id);
 	msg->buf = calloc(args_size, 1);
@@ -840,28 +868,30 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
 
 int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
 				int argc, char **argv, bool use_fifos,
-				unsigned long long trace_id)
+				unsigned long long trace_id,
+				unsigned int tsync_protos)
 {
 	struct tracecmd_msg msg;
 	int ret;
 
 	tracecmd_msg_init(MSG_TRACE_REQ, &msg);
 	ret = make_trace_req(&msg, argc, argv,
-			     use_fifos, trace_id);
+			     use_fifos, trace_id, tsync_protos);
 	if (ret < 0)
 		return ret;
 
 	return tracecmd_msg_send(msg_handle->fd, &msg);
 }
 
- /*
-  * NOTE: On success, the returned `argv` should be freed with:
-  *     free(argv[0]);
-  *     free(argv);
-  */
+/*
+ * NOTE: On success, the returned `argv` should be freed with:
+ *     free(argv[0]);
+ *     free(argv);
+ */
 int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
 				int *argc, char ***argv, bool *use_fifos,
-				unsigned long long *trace_id)
+				unsigned long long *trace_id,
+				unsigned int *tsync_protos)
 {
 	struct tracecmd_msg msg;
 	char *p, *buf_end, **args;
@@ -908,6 +938,7 @@ int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
 	*argc = nr_args;
 	*argv = args;
 	*use_fifos = ntohl(msg.trace_req.flags) & MSG_TRACE_USE_FIFOS;
+	*tsync_protos = ntohl(msg.trace_req.tsync_protos);
 	*trace_id = ntohll(msg.trace_req.trace_id);
 
 	/*
@@ -928,9 +959,81 @@ out:
 	return ret;
 }
 
+int tracecmd_msg_send_time_sync(struct tracecmd_msg_handle *msg_handle,
+				unsigned int sync_protocol,
+				unsigned int sync_msg_id,
+				unsigned int payload_size, char *payload)
+{
+	struct tracecmd_msg msg;
+
+	tracecmd_msg_init(MSG_TIME_SYNC, &msg);
+	msg.tsync.sync_protocol = htonl(sync_protocol);
+	msg.tsync.sync_msg_id = htonl(sync_msg_id);
+	msg.hdr.size = htonl(ntohl(msg.hdr.size) + payload_size);
+
+	msg.buf = payload;
+	return tracecmd_msg_send_nofree(msg_handle->fd, &msg);
+}
+
+int tracecmd_msg_recv_time_sync(struct tracecmd_msg_handle *msg_handle,
+				unsigned int *sync_protocol,
+				unsigned int *sync_msg_id,
+				unsigned int *payload_size, char **payload)
+{
+	struct tracecmd_msg msg;
+	int ret = -1;
+	int buf_size;
+
+	memset(&msg, 0, sizeof(msg));
+	ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+	if (ret < 0)
+		goto out;
+
+	if (ntohl(msg.hdr.cmd) != MSG_TIME_SYNC) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (sync_protocol)
+		*sync_protocol = ntohl(msg.tsync.sync_protocol);
+	if (sync_msg_id)
+		*sync_msg_id = ntohl(msg.tsync.sync_msg_id);
+
+	buf_size = msg_buf_len(&msg);
+	if (buf_size < 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (buf_size && payload && payload_size) {
+		if (*payload_size) {
+			if (*payload_size != buf_size || *payload == NULL) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			memcpy(*payload, msg.buf, buf_size);
+			goto out;
+		}
+
+		*payload = malloc(buf_size);
+		if (*payload == NULL) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		*payload_size = buf_size;
+		memcpy(*payload, msg.buf, buf_size);
+	}
+
+out:
+	msg_free(&msg);
+	return ret;
+}
+
 static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
 			   unsigned int *ports, bool use_fifos,
-			   unsigned long long trace_id)
+			   unsigned long long trace_id,
+			   unsigned int tsync_proto,
+			   unsigned int tsync_port)
 {
 	int data_size;
 
@@ -941,7 +1044,13 @@ static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
 	write_uints(msg->buf, data_size, ports, nr_cpus);
 
 	msg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);
-	msg->trace_resp.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
+	msg->trace_resp.flags = 0;
+	if (use_fifos)
+		msg->trace_resp.flags |= MSG_TRACE_USE_FIFOS;
+	msg->trace_resp.flags = htonl(msg->trace_resp.flags);
+	msg->trace_resp.tsync_proto = htonl(tsync_proto);
+	msg->trace_resp.tsync_port = htonl(tsync_port);
+
 	msg->trace_resp.cpus = htonl(nr_cpus);
 	msg->trace_resp.page_size = htonl(page_size);
 	msg->trace_resp.trace_id = htonll(trace_id);
@@ -952,14 +1061,16 @@ static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
 int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int nr_cpus, int page_size,
 				 unsigned int *ports, bool use_fifos,
-				 unsigned long long trace_id)
+				 unsigned long long trace_id,
+				 unsigned int tsync_proto,
+				 unsigned int tsync_port)
 {
 	struct tracecmd_msg msg;
 	int ret;
 
 	tracecmd_msg_init(MSG_TRACE_RESP, &msg);
 	ret = make_trace_resp(&msg, page_size, nr_cpus, ports,
-			      use_fifos, trace_id);
+			      use_fifos, trace_id, tsync_proto, tsync_port);
 	if (ret < 0)
 		return ret;
 
@@ -969,7 +1080,9 @@ int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int *nr_cpus, int *page_size,
 				 unsigned int **ports, bool *use_fifos,
-				 unsigned long long *trace_id)
+				 unsigned long long *trace_id,
+				 unsigned int *tsync_proto,
+				 unsigned int *tsync_port)
 {
 	struct tracecmd_msg msg;
 	char *p, *buf_end;
@@ -996,6 +1109,8 @@ int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 	*nr_cpus = ntohl(msg.trace_resp.cpus);
 	*page_size = ntohl(msg.trace_resp.page_size);
 	*trace_id = ntohll(msg.trace_resp.trace_id);
+	*tsync_proto = ntohl(msg.trace_resp.tsync_proto);
+	*tsync_port = ntohl(msg.trace_resp.tsync_port);
 	*ports = calloc(*nr_cpus, sizeof(**ports));
 	if (!*ports) {
 		ret = -ENOMEM;
diff --git a/lib/trace-cmd/trace-timesync.c b/lib/trace-cmd/trace-timesync.c
new file mode 100644
index 0000000..073423d
--- /dev/null
+++ b/lib/trace-cmd/trace-timesync.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <linux/vm_sockets.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <dirent.h>
+#include <errno.h>
+#include "trace-cmd.h"
+#include "trace-cmd-local.h"
+#include "trace-tsync-local.h"
+
+struct tsync_proto {
+	struct tsync_proto *next;
+	unsigned int proto_id;
+	int	weight;
+
+	int (*clock_sync_init)(struct tracecmd_time_sync *clock_context);
+	int (*clock_sync_free)(struct tracecmd_time_sync *clock_context);
+	int (*clock_sync_calc)(struct tracecmd_time_sync *clock_context,
+			       long long *offset, long long *timestamp);
+} static * tsync_proto_list;
+
+static struct tsync_proto *tsync_proto_get(unsigned int proto_id)
+{
+	struct tsync_proto *proto = tsync_proto_list;
+
+	while (proto) {
+		if (proto->proto_id == proto_id)
+			return proto;
+		proto = proto->next;
+	}
+	return NULL;
+}
+
+int tracecmd_tsync_proto_register(unsigned int proto_id, int weight,
+				int (*init)(struct tracecmd_time_sync *),
+				int (*free)(struct tracecmd_time_sync *),
+				int (*calc)(struct tracecmd_time_sync *,
+					    long long *, long long *))
+{
+	struct tsync_proto *proto;
+
+	if (tsync_proto_get(proto_id))
+		return -1;
+	proto = calloc(1, sizeof(struct tsync_proto));
+	if (!proto)
+		return -1;
+	proto->proto_id = proto_id;
+	proto->weight = weight;
+	proto->clock_sync_init = init;
+	proto->clock_sync_free = free;
+	proto->clock_sync_calc = calc;
+
+	proto->next = tsync_proto_list;
+	tsync_proto_list = proto;
+	return 0;
+}
+
+int tracecmd_tsync_proto_unregister(unsigned int proto_id)
+{
+	struct tsync_proto *proto = tsync_proto_list;
+	struct tsync_proto *pdel;
+
+	if (tsync_proto_list && tsync_proto_list->proto_id == proto_id) {
+		pdel = tsync_proto_list;
+		tsync_proto_list = pdel->next;
+		free(pdel);
+		return 0;
+	}
+
+	while (proto->next) {
+		if (proto->next->proto_id == proto_id) {
+			pdel = proto->next;
+			proto->next = pdel->next;
+			free(pdel);
+			return 0;
+		}
+		proto = proto->next;
+	}
+
+	return -1;
+}
+
+/**
+ * tracecmd_tsync_get_offsets - Return the calculated time offsests
+ *
+ * @tsync: Pointer to time sync context.
+ * @count: Returns the number of calculated time offsets.
+ * @ts: Array of size @count containing timestamps of callculated offsets
+ * @offsets: array of size @count, containing offsets for each timestamp.
+ *
+ * Retuns -1 in case of an error, or 0 otherwise
+ */
+int tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync,
+				int *count,
+				long long **ts, long long **offsets)
+{
+	struct clock_sync_context *tsync_context;
+
+	if (!tsync || !tsync->context)
+		return -1;
+	tsync_context = (struct clock_sync_context *)tsync->context;
+	if (count)
+		*count = tsync_context->sync_count;
+	if (ts)
+		*ts = tsync_context->sync_ts;
+	if (offsets)
+		*offsets = tsync_context->sync_offsets;
+	return 0;
+}
+
+/**
+ * tracecmd_tsync_proto_select - Select time sync protocol, to be used for
+ *		timestamp synchronization with a peer.
+ *
+ * @protos: Bitmask of time sync protocols, supported by the peer.
+ *
+ * Retuns Id of a time sync protocol, that can be used with the peer, or 0
+ *	in case there is no match with supported protocols.
+ */
+unsigned int tracecmd_tsync_proto_select(unsigned int protos)
+{
+	struct tsync_proto *proto = tsync_proto_list;
+	struct tsync_proto *selected = NULL;
+
+	while (proto) {
+		if (proto->proto_id & protos) {
+			if (selected) {
+				if (selected->weight < proto->weight)
+					selected = proto;
+			} else
+				selected = proto;
+		}
+		proto = proto->next;
+	}
+
+	if (selected)
+		return selected->proto_id;
+
+	return 0;
+}
+
+/**
+ * tracecmd_tsync_proto_getall - Returns bitmask of all supported
+ *				 time sync protocols.
+ *
+ */
+unsigned int tracecmd_tsync_proto_getall(void)
+{
+	struct tsync_proto *proto = tsync_proto_list;
+	unsigned int protos = 0;
+
+	while (proto) {
+		protos |= proto->proto_id;
+		proto = proto->next;
+	}
+
+	return protos;
+}
+
+static int get_vsocket_params(int fd, unsigned int *lcid, unsigned int *lport,
+			      unsigned int *rcid, unsigned int *rport)
+{
+	struct sockaddr_vm addr;
+	socklen_t addr_len = sizeof(addr);
+
+	memset(&addr, 0, sizeof(addr));
+	if (getsockname(fd, (struct sockaddr *)&addr, &addr_len))
+		return -1;
+	if (addr.svm_family != AF_VSOCK)
+		return -1;
+	*lport = addr.svm_port;
+	*lcid = addr.svm_cid;
+
+	memset(&addr, 0, sizeof(addr));
+	addr_len = sizeof(addr);
+	if (getpeername(fd, (struct sockaddr *)&addr, &addr_len))
+		return -1;
+	if (addr.svm_family != AF_VSOCK)
+		return -1;
+	*rport = addr.svm_port;
+	*rcid = addr.svm_cid;
+
+	return 0;
+}
+
+static struct tracecmd_instance *
+clock_synch_create_instance(const char *clock, unsigned int cid)
+{
+	struct tracecmd_instance *vinst;
+	char inst_name[256];
+
+	snprintf(inst_name, 256, "clock_synch-%d", cid);
+
+	vinst = tracecmd_create_instance(inst_name);
+	if (!vinst)
+		return NULL;
+
+	tracecmd_make_instance(vinst);
+	tracecmd_write_instance_file(vinst, "trace", "\0", NULL);
+	if (clock)
+		vinst->clock = strdup(clock);
+	tracecmd_set_clock(vinst, NULL);
+	return vinst;
+}
+
+static void
+clock_synch_delete_instance(struct tracecmd_instance *inst)
+{
+	if (!inst)
+		return;
+	tracecmd_remove_instance(inst);
+	free(inst->clock);
+	tracecmd_free_instance(inst);
+}
+
+static int clock_context_init(struct tracecmd_time_sync *tsync, bool server)
+{
+	struct clock_sync_context *clock = NULL;
+	struct tsync_proto *protocol;
+
+	if (tsync->context)
+		return 0;
+
+	protocol = tsync_proto_get(tsync->sync_proto);
+	if (!protocol)
+		return -1;
+
+	clock = calloc(1, sizeof(struct clock_sync_context));
+	if (!clock)
+		return -1;
+
+	clock->server = server;
+	if (get_vsocket_params(tsync->msg_handle->fd, &clock->local_cid,
+			       &clock->local_port, &clock->remote_cid,
+			       &clock->remote_port))
+		goto error;
+
+	clock->vinst = clock_synch_create_instance(tsync->clock_str,
+						   clock->remote_cid);
+	if (!clock->vinst)
+		goto error;
+
+	tsync->context = clock;
+	if (protocol->clock_sync_init && protocol->clock_sync_init(tsync) < 0)
+		goto error;
+
+	return 0;
+error:
+	tsync->context = NULL;
+	free(clock);
+	return -1;
+}
+
+/**
+ * tracecmd_tsync_free - Free time sync context, allocated by
+ *		tracecmd_tsync_with_host() or tracecmd_tsync_with_guest() APIs
+ *
+ * @tsync: Pointer to time sync context.
+ *
+ */
+void tracecmd_tsync_free(struct tracecmd_time_sync *tsync)
+{
+	struct clock_sync_context *tsync_context;
+	struct tsync_proto *proto;
+
+	if (!tsync->context)
+		return;
+	tsync_context = (struct clock_sync_context *)tsync->context;
+
+	proto = tsync_proto_get(tsync->sync_proto);
+	if (proto && proto->clock_sync_free)
+		proto->clock_sync_free(tsync);
+
+	clock_synch_delete_instance(tsync_context->vinst);
+	tsync_context->vinst = NULL;
+
+	free(tsync_context->sync_ts);
+	free(tsync_context->sync_offsets);
+	tsync_context->sync_ts = NULL;
+	tsync_context->sync_offsets = NULL;
+	tsync_context->sync_count = 0;
+	tsync_context->sync_size = 0;
+	sem_destroy(&tsync->sem);
+	free(tsync->clock_str);
+}
+
+static int tracecmd_tsync_send(struct tracecmd_time_sync *tsync,
+				  struct tsync_proto *proto)
+{
+	long long timestamp = 0;
+	long long offset = 0;
+	int ret;
+
+	ret = proto->clock_sync_calc(tsync, &offset, &timestamp);
+
+	return ret;
+}
+
+/**
+ * tracecmd_tsync_with_host - Synchronize timestamps with host
+ *
+ * @tsync: Pointer to time sync context.
+ *
+ * This API is supposed to be called in guest context. It waits for a time
+ * sync request from the host and replies with a time sample, until time sync
+ * stop command is received.
+ *
+ */
+void tracecmd_tsync_with_host(struct tracecmd_time_sync *tsync)
+{
+	struct tsync_proto *proto;
+	unsigned int protocol;
+	unsigned int command;
+	int ret;
+
+	proto = tsync_proto_get(tsync->sync_proto);
+	if (!proto || !proto->clock_sync_calc)
+		return;
+
+	clock_context_init(tsync, true);
+	if (!tsync->context)
+		return;
+
+	while (true) {
+		ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+						  &protocol, &command,
+						  NULL, NULL);
+
+		if (ret ||
+		    protocol != TRACECMD_TIME_SYNC_PROTO_NONE ||
+		    command != TRACECMD_TIME_SYNC_CMD_PROBE)
+			break;
+		ret = tracecmd_tsync_send(tsync, proto);
+		if (ret)
+			break;
+	}
+}
+
+static int tracecmd_tsync_get(struct tracecmd_time_sync *tsync,
+			      struct tsync_proto *proto,
+			      int array_step)
+{
+	struct clock_sync_context *clock;
+	long long *sync_offsets = NULL;
+	long long *sync_ts = NULL;
+	long long timestamp = 0;
+	long long offset = 0;
+	int ret;
+
+	ret = proto->clock_sync_calc(tsync, &offset, &timestamp);
+	if (ret) {
+		warning("Failed to synchronize timestamps with guest");
+		return -1;
+	}
+	if (!offset || !timestamp)
+		return 0;
+	clock = tsync->context;
+	if (clock->sync_count >= clock->sync_size) {
+		sync_ts = realloc(clock->sync_ts,
+				  (clock->sync_size + array_step) * sizeof(long long));
+		sync_offsets = realloc(clock->sync_offsets,
+				       (clock->sync_size + array_step) * sizeof(long long));
+		if (!sync_ts || !sync_offsets) {
+			free(sync_ts);
+			free(sync_offsets);
+			return -1;
+		}
+		clock->sync_size += array_step;
+		clock->sync_ts = sync_ts;
+		clock->sync_offsets = sync_offsets;
+	}
+
+	clock->sync_ts[clock->sync_count] = timestamp;
+	clock->sync_offsets[clock->sync_count] = offset;
+	clock->sync_count++;
+
+	return 0;
+}
+
+#define TIMER_SEC_NANO 1000000000LL
+static inline void get_ts_loop_delay(struct timespec *timeout, int delay_ms)
+{
+	memset(timeout, 0, sizeof(struct timespec));
+	clock_gettime(CLOCK_REALTIME, timeout);
+
+	timeout->tv_nsec += ((unsigned long long)delay_ms * 1000000LL);
+
+	if (timeout->tv_nsec >= TIMER_SEC_NANO) {
+		timeout->tv_sec += timeout->tv_nsec / TIMER_SEC_NANO;
+		timeout->tv_nsec %= TIMER_SEC_NANO;
+	}
+}
+
+#define CLOCK_TS_ARRAY 5
+/**
+ * tracecmd_tsync_with_guest - Synchronize timestamps with guest
+ *
+ * @tsync: Pointer to time sync context.
+ *
+ * This API is supposed to be called in host context, in a separate thread.
+ * It loops infinite, until the timesync semaphore is released.
+ *
+ */
+void tracecmd_tsync_with_guest(struct tracecmd_time_sync *tsync)
+{
+	int ts_array_size = CLOCK_TS_ARRAY;
+	struct tsync_proto *proto;
+	struct timespec timeout;
+	bool end = false;
+	int ret;
+
+	proto = tsync_proto_get(tsync->sync_proto);
+	if (!proto || !proto->clock_sync_calc)
+		return;
+
+	clock_context_init(tsync, false);
+	if (!tsync->context)
+		return;
+
+	if (tsync->loop_interval > 0 &&
+	    tsync->loop_interval < (CLOCK_TS_ARRAY * 1000))
+		ts_array_size = (CLOCK_TS_ARRAY * 1000) / tsync->loop_interval;
+
+	while (true) {
+		ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+						  TRACECMD_TIME_SYNC_PROTO_NONE,
+						  TRACECMD_TIME_SYNC_CMD_PROBE,
+						  0, NULL);
+		ret = tracecmd_tsync_get(tsync, proto, ts_array_size);
+		if (ret || end)
+			break;
+		if (tsync->loop_interval > 0) {
+			get_ts_loop_delay(&timeout, tsync->loop_interval);
+			ret = sem_timedwait(&tsync->sem, &timeout);
+			if (ret < 0) {
+				if (errno != ETIMEDOUT && errno != EINTR)
+					break;
+			} else if (!ret)
+				end = true;
+		} else {
+			sem_wait(&tsync->sem);
+			end = true;
+		}
+	};
+
+	tracecmd_msg_send_time_sync(tsync->msg_handle,
+				    TRACECMD_TIME_SYNC_PROTO_NONE,
+				    TRACECMD_TIME_SYNC_CMD_STOP,
+				    0, NULL);
+}
diff --git a/tracecmd/Makefile b/tracecmd/Makefile
index 29a623b..908dab2 100644
--- a/tracecmd/Makefile
+++ b/tracecmd/Makefile
@@ -30,6 +30,7 @@ TRACE_CMD_OBJS += trace-check-events.o
 TRACE_CMD_OBJS += trace-show.o
 TRACE_CMD_OBJS += trace-list.o
 TRACE_CMD_OBJS += trace-usage.o
+TRACE_CMD_OBJS += trace-tsync.o
 
 ifeq ($(VSOCK_DEFINED), 1)
 TRACE_CMD_OBJS += trace-agent.o
@@ -42,7 +43,7 @@ all_objs := $(sort $(ALL_OBJS))
 all_deps := $(all_objs:$(bdir)/%.o=$(bdir)/.%.d)
 
 CONFIG_INCLUDES =
-CONFIG_LIBS	= -lrt
+CONFIG_LIBS	= -lrt -lpthread
 CONFIG_FLAGS	=
 
 all: $(TARGETS)
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index 17ef31a..d652317 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -226,6 +226,10 @@ struct buffer_instance {
 	unsigned int		port;
 	int			*fds;
 	bool			use_fifos;
+
+	pthread_t		tsync_thread;
+	bool			tsync_thread_running;
+	struct tracecmd_time_sync tsync;
 };
 
 void init_top_instance(void);
@@ -260,6 +264,13 @@ void tracecmd_disable_tracing(void);
 void tracecmd_enable_tracing(void);
 void tracecmd_stat_cpu(struct trace_seq *s, int cpu);
 
+int tracecmd_host_tsync(struct buffer_instance *instance,
+			 unsigned int tsync_port);
+void tracecmd_host_tsync_complete(struct buffer_instance *instance);
+unsigned int tracecmd_guest_tsync(unsigned int tsync_protos, char *clock,
+				  unsigned int *tsync_port,
+				  pthread_t *thr_id);
+
 int make_vsock(unsigned int port);
 int get_vsock_port(int sd, unsigned int *port);
 int open_vsock(unsigned int cid, unsigned int port);
diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
index 530f972..1581de8 100644
--- a/tracecmd/trace-agent.c
+++ b/tracecmd/trace-agent.c
@@ -19,6 +19,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 #include <linux/vm_sockets.h>
+#include <pthread.h>
 
 #include "trace-local.h"
 #include "trace-msg.h"
@@ -125,11 +126,28 @@ cleanup:
 	return ret;
 }
 
+static char *get_clock(int argc, char **argv)
+{
+	int i;
+
+	if (!argc || !argv)
+		return NULL;
+
+	for (i = 0; i < argc - 1; i++) {
+		if (!strcmp("-C", argv[i]))
+			return argv[i+1];
+	}
+	return NULL;
+}
+
 static void agent_handle(int sd, int nr_cpus, int page_size)
 {
 	struct tracecmd_msg_handle *msg_handle;
 	unsigned long long trace_id;
+	unsigned int tsync_protos = 0;
+	unsigned int tsync_port = 0;
 	unsigned int *ports;
+	pthread_t sync_thr;
 	char **argv = NULL;
 	int argc = 0;
 	bool use_fifos;
@@ -146,7 +164,8 @@ static void agent_handle(int sd, int nr_cpus, int page_size)
 		die("Failed to allocate message handle");
 
 	ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
-					  &use_fifos, &trace_id);
+					  &use_fifos, &trace_id,
+					  &tsync_protos);
 	if (ret < 0)
 		die("Failed to receive trace request");
 
@@ -155,14 +174,25 @@ static void agent_handle(int sd, int nr_cpus, int page_size)
 
 	if (!use_fifos)
 		make_vsocks(nr_cpus, fds, ports);
+	if (tsync_protos) {
+		tsync_protos = tracecmd_guest_tsync(tsync_protos,
+						    get_clock(argc, argv),
+						    &tsync_port, &sync_thr);
+		if (!tsync_protos)
+			warning("Failed to negotiate timestamps synchronization with the host");
+	}
 	trace_id = tracecmd_generate_traceid();
 	ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
-					   ports, use_fifos, trace_id);
+					   ports, use_fifos, trace_id,
+					   tsync_protos, tsync_port);
 	if (ret < 0)
 		die("Failed to send trace response");
 	trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
 			   use_fifos, trace_id);
 
+	if (tsync_protos)
+		pthread_join(sync_thr, NULL);
+
 	free(argv[0]);
 	free(argv);
 	free(ports);
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index e66f999..e543c40 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -670,6 +670,11 @@ static void tell_guests_to_stop(void)
 			tracecmd_msg_send_close_msg(instance->msg_handle);
 	}
 
+	for_all_instances(instance) {
+		if (is_guest(instance))
+			tracecmd_host_tsync_complete(instance);
+	}
+
 	/* Wait for guests to acknowledge */
 	for_all_instances(instance) {
 		if (is_guest(instance)) {
@@ -3501,6 +3506,8 @@ static void connect_to_agent(struct buffer_instance *instance)
 {
 	struct tracecmd_msg_handle *msg_handle;
 	int sd, ret, nr_fifos, nr_cpus, page_size;
+	unsigned int tsync_protos_reply = 0;
+	unsigned int tsync_port = 0;
 	unsigned int *ports;
 	int i, *fds = NULL;
 	bool use_fifos = false;
@@ -3519,19 +3526,32 @@ static void connect_to_agent(struct buffer_instance *instance)
 	if (!msg_handle)
 		die("Failed to allocate message handle");
 
+	if (instance->tsync.loop_interval >= 0)
+		instance->tsync.sync_proto = tracecmd_tsync_proto_getall();
+
 	ret = tracecmd_msg_send_trace_req(msg_handle, instance->argc,
 					  instance->argv, use_fifos,
-					  top_instance.trace_id);
+					  top_instance.trace_id,
+					  instance->tsync.sync_proto);
 
 	if (ret < 0)
 		die("Failed to send trace request");
 
 	ret = tracecmd_msg_recv_trace_resp(msg_handle, &nr_cpus, &page_size,
 					   &ports, &use_fifos,
-					   &instance->trace_id);
+					   &instance->trace_id,
+					   &tsync_protos_reply, &tsync_port);
 
 	if (ret < 0)
-		die("Failed to receive trace response");
+		die("Failed to receive trace response %d", ret);
+
+	if (instance->tsync.sync_proto) {
+		if (instance->tsync.sync_proto & tsync_protos_reply) {
+			instance->tsync.sync_proto = tsync_protos_reply;
+			tracecmd_host_tsync(instance, tsync_port);
+		} else
+			warning("Failed to negotiate timestamps synchronization with the guest");
+	}
 
 	if (use_fifos) {
 		if (nr_cpus != nr_fifos) {
@@ -3578,7 +3598,9 @@ static void setup_guest(struct buffer_instance *instance)
 	close(fd);
 }
 
-static void setup_agent(struct buffer_instance *instance, struct common_record_context *ctx)
+static void setup_agent(struct buffer_instance *instance,
+			struct common_record_context *ctx)
+
 {
 	struct tracecmd_output *network_handle;
 
@@ -5131,6 +5153,7 @@ void init_top_instance(void)
 }
 
 enum {
+	OPT_tsyncinterval	= 242,
 	OPT_user		= 243,
 	OPT_procmap		= 244,
 	OPT_quiet		= 245,
@@ -5433,6 +5456,7 @@ static void parse_record_options(int argc,
 			{"proc-map", no_argument, NULL, OPT_procmap},
 			{"user", required_argument, NULL, OPT_user},
 			{"module", required_argument, NULL, OPT_module},
+			{"tsync-interval", required_argument, NULL, OPT_tsyncinterval},
 			{NULL, 0, NULL, 0}
 		};
 
@@ -5771,6 +5795,10 @@ static void parse_record_options(int argc,
 			ctx->instance->filter_mod = optarg;
 			ctx->filtered = 0;
 			break;
+		case OPT_tsyncinterval:
+			top_instance.tsync.loop_interval = atoi(optarg);
+			guest_config = true;
+			break;
 		case OPT_quiet:
 		case 'q':
 			quiet = true;
@@ -5799,6 +5827,7 @@ static void parse_record_options(int argc,
 					add_argv(instance, "-C", true);
 				}
 			}
+		instance->tsync.loop_interval = top_instance.tsync.loop_interval;
 		}
 	}
 
diff --git a/tracecmd/trace-tsync.c b/tracecmd/trace-tsync.c
new file mode 100644
index 0000000..e4189e0
--- /dev/null
+++ b/tracecmd/trace-tsync.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <linux/vm_sockets.h>
+#include <pthread.h>
+
+#include "trace-local.h"
+#include "trace-msg.h"
+
+static int get_first_cpu(cpu_set_t **pin_mask, size_t *m_size)
+{
+	int cpus = tracecmd_count_cpus();
+	cpu_set_t *cpu_mask;
+	int mask_size;
+	int i;
+
+	cpu_mask = CPU_ALLOC(cpus);
+	*pin_mask = CPU_ALLOC(cpus);
+	if (!cpu_mask || !*pin_mask || 1)
+		goto error;
+
+	mask_size = CPU_ALLOC_SIZE(cpus);
+	CPU_ZERO_S(mask_size, cpu_mask);
+	CPU_ZERO_S(mask_size, *pin_mask);
+
+	if (sched_getaffinity(0, mask_size, cpu_mask) == -1)
+		goto error;
+
+	for (i = 0; i < cpus; i++) {
+		if (CPU_ISSET_S(i, mask_size, cpu_mask)) {
+			CPU_SET_S(i, mask_size, *pin_mask);
+			break;
+		}
+	}
+
+	if (CPU_COUNT_S(mask_size, *pin_mask) < 1)
+		goto error;
+
+	CPU_FREE(cpu_mask);
+	*m_size = mask_size;
+	return 0;
+
+error:
+	if (cpu_mask)
+		CPU_FREE(cpu_mask);
+	if (*pin_mask)
+		CPU_FREE(*pin_mask);
+	*pin_mask = NULL;
+	*m_size = 0;
+	return -1;
+}
+
+static void *tsync_host_thread(void *data)
+{
+	struct tracecmd_time_sync *tsync = NULL;
+
+	tsync = (struct tracecmd_time_sync *)data;
+
+	tracecmd_tsync_with_guest(tsync);
+
+	tracecmd_msg_handle_close(tsync->msg_handle);
+	tsync->msg_handle = NULL;
+
+	pthread_exit(0);
+}
+
+int tracecmd_host_tsync(struct buffer_instance *instance,
+			 unsigned int tsync_port)
+{
+	struct tracecmd_msg_handle *msg_handle = NULL;
+	cpu_set_t *pin_mask = NULL;
+	pthread_attr_t attrib;
+	size_t mask_size = 0;
+	int ret;
+	int fd;
+
+	if (!instance->tsync.sync_proto)
+		return -1;
+
+	fd = open_vsock(instance->cid, tsync_port);
+	if (fd < 0) {
+		ret = -1;
+		goto out;
+	}
+	msg_handle = tracecmd_msg_handle_alloc(fd, 0);
+	if (!msg_handle) {
+		ret = -1;
+		goto out;
+	}
+
+	instance->tsync.msg_handle = msg_handle;
+	if (top_instance.ftrace->clock)
+		instance->tsync.clock_str = strdup(top_instance.ftrace->clock);
+	ret = sem_init(&instance->tsync.sem, 0, 0);
+	if (ret)
+		goto out;
+
+	pthread_attr_init(&attrib);
+	pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
+	if (!get_first_cpu(&pin_mask, &mask_size))
+		pthread_attr_setaffinity_np(&attrib, mask_size, pin_mask);
+
+	ret = pthread_create(&instance->tsync_thread, &attrib,
+			     tsync_host_thread, &instance->tsync);
+	if (!ret)
+		instance->tsync_thread_running = true;
+	if (pin_mask)
+		CPU_FREE(pin_mask);
+	pthread_attr_destroy(&attrib);
+
+out:
+	if (ret) {
+		if (msg_handle)
+			tracecmd_msg_handle_close(msg_handle);
+	}
+
+	return ret;
+}
+
+static void write_guest_time_shift(struct buffer_instance *instance)
+{
+	struct tracecmd_output *handle;
+	struct iovec vector[4];
+	long long *offsets;
+	long long *ts;
+	char *file;
+	int count;
+	int ret;
+	int fd;
+
+	ret = tracecmd_tsync_get_offsets(&instance->tsync, &count, &ts, &offsets);
+	if (ret < 0 || !count || !ts || !offsets)
+		return;
+
+	file = get_guest_file(TRACE_FILENAME, instance->ftrace->name);
+	fd = open(file, O_RDWR);
+	if (fd < 0)
+		die("error opening %s", file);
+	free(file);
+	handle = tracecmd_get_output_handle_fd(fd);
+	vector[0].iov_len = 8;
+	vector[0].iov_base = &top_instance.trace_id;
+	vector[1].iov_len = 4;
+	vector[1].iov_base = &count;
+	vector[2].iov_len = 8 * count;
+	vector[2].iov_base = ts;
+	vector[3].iov_len = 8 * count;
+	vector[3].iov_base = offsets;
+	tracecmd_add_option_v(handle, TRACECMD_OPTION_TIME_SHIFT, vector, 4);
+	tracecmd_append_options(handle);
+	tracecmd_output_close(handle);
+#ifdef TSYNC_DEBUG
+	if (count > 1)
+		printf("Got %d timestamp synch samples for guest %s in %lld ns trace\n\r",
+			count, instance->ftrace->name, ts[count - 1] - ts[0]);
+#endif
+}
+
+void tracecmd_host_tsync_complete(struct buffer_instance *instance)
+{
+	if (!instance->tsync_thread_running)
+		return;
+
+	sem_post(&instance->tsync.sem);
+	pthread_join(instance->tsync_thread, NULL);
+	write_guest_time_shift(instance);
+	tracecmd_tsync_free(&instance->tsync);
+}
+
+static void *tsync_agent_thread(void *data)
+{
+	struct tracecmd_time_sync *tsync = NULL;
+	int sd;
+
+	tsync = (struct tracecmd_time_sync *)data;
+
+	while (true) {
+		sd = accept(tsync->msg_handle->fd, NULL, NULL);
+		if (sd < 0) {
+			if (errno == EINTR)
+				continue;
+			goto out;
+		}
+		break;
+	}
+	close(tsync->msg_handle->fd);
+	tsync->msg_handle->fd = sd;
+
+	tracecmd_tsync_with_host(tsync);
+
+out:
+	tracecmd_msg_handle_close(tsync->msg_handle);
+	tracecmd_tsync_free(tsync);
+	free(tsync);
+	close(sd);
+
+	pthread_exit(0);
+}
+
+unsigned int tracecmd_guest_tsync(unsigned int tsync_protos, char *clock,
+				  unsigned int *tsync_port,
+				  pthread_t *thr_id)
+{
+	struct tracecmd_time_sync *tsync = NULL;
+	cpu_set_t *pin_mask = NULL;
+	pthread_attr_t attrib;
+	size_t mask_size = 0;
+	unsigned int proto;
+	int ret;
+	int fd;
+
+	fd = -1;
+	proto = tracecmd_tsync_proto_select(tsync_protos);
+	if (!proto)
+		return 0;
+#ifdef VSOCK
+	fd = make_vsock(VMADDR_PORT_ANY);
+	if (fd < 0)
+		goto error;
+
+	ret = get_vsock_port(fd, tsync_port);
+	if (ret < 0)
+		goto error;
+#else
+	return 0;
+#endif
+
+	tsync = calloc(1, sizeof(struct tracecmd_time_sync));
+	tsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);
+	if (clock)
+		tsync->clock_str = strdup(clock);
+
+	pthread_attr_init(&attrib);
+	tsync->sync_proto = proto;
+	pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
+	if (!get_first_cpu(&pin_mask, &mask_size))
+		pthread_attr_setaffinity_np(&attrib, mask_size, pin_mask);
+
+	ret = pthread_create(thr_id, &attrib, tsync_agent_thread, tsync);
+
+	if (pin_mask)
+		CPU_FREE(pin_mask);
+	pthread_attr_destroy(&attrib);
+
+	if (ret)
+		goto error;
+
+	return proto;
+
+error:
+	if (tsync) {
+		if (tsync->msg_handle)
+			tracecmd_msg_handle_close(tsync->msg_handle);
+		if (tsync->clock_str)
+			free(tsync->clock_str);
+		free(tsync);
+	}
+	if (fd > 0)
+		close(fd);
+	return 0;
+}
diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
index 05ec021..9fa61e1 100644
--- a/tracecmd/trace-usage.c
+++ b/tracecmd/trace-usage.c
@@ -60,6 +60,10 @@ static struct usage_help usage_help[] = {
 		"          --no-filter include trace-cmd threads in the trace\n"
 		"          --proc-map save the traced processes address map into the trace.dat file\n"
 		"          --user execute the specified [command ...] as given user\n"
+		"          --tsync-interval set the loop interval, in ms, for timestamps synchronization with guests:"
+		"               If a negative number is specified, timestamps synchronization is disabled"
+		"               If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
+		"                                                         at the beginnig and at the end of the trace\n"
 	},
 	{
 		"start",
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 17/18] trace-cmd: [POC] PTP-like algorithm for host - guest timestamp synchronization
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (15 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  2019-12-03 10:35 ` [PATCH v17 18/18] trace-cmd: Debug scripts for " Tzvetomir Stoyanov (VMware)
  17 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

PTP protocol is designed for synchronizing clocks of machines in a local network.
The same approach can be used for host - guest timestamp synchronization.
This implementation uses ftrace raw markers to track trace timestamps of PTP events.
The patch is a POC, two different algorithms for PTP calculations are proposed:
  - Choosing the sample with the fastest response time for calculating the clocks offset.
  - Calculating the clocks offset using the average of all PTP samples.

The implementation can be tuned using those parameters:
 - #define FASTEST_RESPONSE - is defined, the sample with the fastest response time
    is used for calculating the clocks offset. Otherwise the histogram of all samples is used.
 - #define PTP_SYNC_LOOP 340 - defines the number of samples, used for one calculation.
 - --tsync-interval - a trace-cmd argument, choose the intervals between offset calculations,
	performed continuously during the trace.
 - #define TSYNC_DEBUG - if defined, a debug information is collected and stored in files,
   in the guest machine:
     s-cid*.txt - For each offset calculation: host and guest clocks and calculated offset.
     res-cid*.txt - For each tracing session: all calculated clock offsets.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/trace-cmd/trace-cmd.h      |   2 +
 lib/trace-cmd/Makefile             |   1 +
 lib/trace-cmd/trace-timesync-ptp.c | 649 +++++++++++++++++++++++++++++
 lib/trace-cmd/trace-timesync.c     |   8 +
 tracecmd/trace-agent.c             |   2 +
 tracecmd/trace-record.c            |  25 +-
 6 files changed, 679 insertions(+), 8 deletions(-)
 create mode 100644 lib/trace-cmd/trace-timesync-ptp.c

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 6abcc9f..013160a 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -427,6 +427,7 @@ int tracecmd_iterate_raw_events(struct tep_handle *tep,
 
 enum{
 	TRACECMD_TIME_SYNC_PROTO_NONE	= 0,
+	TRACECMD_TIME_SYNC_PROTO_PTP	= 1 << 0,
 };
 enum{
 	TRACECMD_TIME_SYNC_CMD_PROBE	= 1,
@@ -444,6 +445,7 @@ struct tracecmd_time_sync {
 	void				*context;
 };
 
+void tracecmd_tsync_init(void);
 unsigned int tracecmd_tsync_proto_getall(void);
 unsigned int tracecmd_tsync_proto_select(unsigned int protos);
 void tracecmd_tsync_with_host(struct tracecmd_time_sync *tsync);
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 6221b36..a2b9d08 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -17,6 +17,7 @@ OBJS += trace-instance.o
 OBJS += trace-filter-hash.o
 OBJS += trace-msg.o
 OBJS += trace-timesync.o
+OBJS += trace-timesync-ptp.o
 
 # Additional util objects
 OBJS += trace-blk-hack.o
diff --git a/lib/trace-cmd/trace-timesync-ptp.c b/lib/trace-cmd/trace-timesync-ptp.c
new file mode 100644
index 0000000..6a5f5b7
--- /dev/null
+++ b/lib/trace-cmd/trace-timesync-ptp.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
+ *
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <linux/vm_sockets.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <time.h>
+#include <sched.h>
+#include "trace-cmd.h"
+#include "trace-tsync-local.h"
+#include "trace-msg.h"
+
+typedef __be32 be32;
+typedef __u64 u64;
+typedef __s64 s64;
+
+#define PTP_SYNC_LOOP	340
+
+#define PTP_SYNC_PKT_START	1
+#define PTP_SYNC_PKT_PROBE	2
+#define PTP_SYNC_PKT_PROBES	3
+#define PTP_SYNC_PKT_OFFSET	4
+#define PTP_SYNC_PKT_END	5
+
+#define TSYNC_DEBUG
+
+struct ptp_clock_sync {
+	struct tep_handle	*tep;
+	int			raw_id;
+	int			marker_fd;
+	int			series_id;
+	int			debug_fd;
+
+};
+
+struct ptp_clock_start_msg {
+	be32	series_id;
+} __attribute__((packed));
+
+struct ptp_clock_sample {
+	s64		ts;
+	be32		id;
+} __attribute__((packed));
+
+struct ptp_clock_result_msg {
+	be32			series_id;
+	be32			count;
+	struct ptp_clock_sample	samples[2*PTP_SYNC_LOOP];
+} __attribute__((packed));
+
+struct ptp_clock_offset_msg {
+	s64	ts;
+	s64	offset;
+};
+
+struct ptp_markers_context {
+	struct clock_sync_context	*clock;
+	struct ptp_clock_sync		*ptp;
+	int				size;
+	struct ptp_clock_result_msg	msg;
+};
+
+struct ptp_marker_buf {
+	int local_cid;
+	int remote_cid;
+	int count;
+	int packet_id;
+} __attribute__((packed));
+
+struct ptp_marker{
+	int series_id;
+	struct ptp_marker_buf data;
+} __attribute__((packed));
+
+static int ptp_clock_sync_init(struct tracecmd_time_sync *tsync)
+{
+	const char *systems[] = { "ftrace", NULL};
+	struct clock_sync_context *clock_context;
+	struct ptp_clock_sync *ptp;
+	struct tep_event *raw;
+	char *path;
+
+	if (!tsync || !tsync->context)
+		return -1;
+	clock_context = (struct clock_sync_context *)tsync->context;
+	if (clock_context->proto_data)
+		return 0;
+
+	ptp = calloc(1, sizeof(struct ptp_clock_sync));
+	if (!ptp)
+		return -1;
+
+	ptp->marker_fd = -1;
+	ptp->debug_fd = -1;
+
+	path = tracecmd_get_instance_dir(clock_context->vinst);
+	if (!path)
+		goto error;
+	ptp->tep = tracecmd_local_events_system(path, systems);
+	tracecmd_put_tracing_file(path);
+	if (!ptp->tep)
+		goto error;
+	raw = tep_find_event_by_name(ptp->tep, "ftrace", "raw_data");
+	if (!raw)
+		goto error;
+
+	ptp->raw_id = raw->id;
+	tep_set_file_bigendian(ptp->tep, tracecmd_host_bigendian());
+	tep_set_local_bigendian(ptp->tep, tracecmd_host_bigendian());
+
+	path = tracecmd_get_instance_file(clock_context->vinst, "trace_marker_raw");
+	if (!path)
+		goto error;
+	ptp->marker_fd = open(path, O_WRONLY);
+	tracecmd_put_tracing_file(path);
+
+	clock_context->proto_data = ptp;
+
+#ifdef TSYNC_DEBUG
+	if (clock_context->server) {
+		char buff[256];
+		int res_fd;
+
+		sprintf(buff, "res-cid%d.txt", clock_context->remote_cid);
+
+		res_fd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644);
+		if (res_fd > 0)
+			close(res_fd);
+	}
+#endif
+
+	return 0;
+
+error:
+	if (ptp) {
+		tep_free(ptp->tep);
+		if (ptp->marker_fd >= 0)
+			close(ptp->marker_fd);
+	}
+	free(ptp);
+	return -1;
+}
+
+static int ptp_clock_sync_free(struct tracecmd_time_sync *tsync)
+{
+	struct clock_sync_context *clock_context;
+	struct ptp_clock_sync *ptp;
+
+	if (!tsync || !tsync->context)
+		return -1;
+	clock_context = (struct clock_sync_context *)tsync->context;
+
+	if (clock_context && clock_context->proto_data) {
+		ptp = (struct ptp_clock_sync *)clock_context->proto_data;
+		tep_free(ptp->tep);
+		if (ptp->marker_fd >= 0)
+			close(ptp->marker_fd);
+		if (ptp->debug_fd >= 0)
+			close(ptp->debug_fd);
+		free(clock_context->proto_data);
+		clock_context->proto_data = NULL;
+	}
+	return 0;
+}
+
+static int ptp_marker_find(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *context)
+{
+	struct ptp_markers_context *ctx;
+	struct tep_format_field *field;
+	struct tep_format_field *id;
+	struct ptp_marker *marker;
+	int index = -1;
+
+	ctx = (struct ptp_markers_context *)context;
+
+	/* Make sure this is our event */
+	if (event->id != ctx->ptp->raw_id)
+		return 0;
+	id = tep_find_field(event, "id");
+	field = tep_find_field(event, "buf");
+	if (field && id &&
+	    record->size >= (id->offset + sizeof(struct ptp_marker))) {
+		marker = (struct ptp_marker *)(record->data + id->offset);
+		if (marker->data.local_cid == ctx->clock->local_cid &&
+		    marker->data.remote_cid == ctx->clock->remote_cid &&
+		    marker->series_id == ctx->ptp->series_id &&
+		    marker->data.count) {
+			if (marker->data.packet_id == 'r' &&
+			    marker->data.count <= ctx->size) {
+				index = marker->data.count - 1;
+			} else if (marker->data.packet_id == 's' &&
+				  marker->data.count*2 <= ctx->size){
+				index = ctx->size / 2 + marker->data.count - 1;
+			}
+		}
+		if (index >= 0) {
+			ctx->msg.samples[index].id = marker->data.count;
+			ctx->msg.samples[index].ts = record->ts;
+			ctx->msg.count++;
+		}
+	}
+
+	return 0;
+}
+
+//#define FASTEST_RESPONSE
+
+#ifdef FASTEST_RESPONSE
+static int ptp_calc_offset(struct clock_sync_context *clock,
+			   struct ptp_clock_result_msg *server,
+			   struct ptp_clock_result_msg *client,
+			   long long *offset_ret, long long *ts_ret,
+			   int *bad_probes)
+{
+	struct ptp_clock_sample *sample_send;
+	long long delta_min = 0;
+	long long offset = 0;
+	long long delta = 0;
+	long long ts = 0;
+	int i;
+
+	*bad_probes = 0;
+	sample_send = server->samples + (server->count / 2);
+	for (i = 0; i * 2 < server->count && i < client->count; i++) {
+		if (!server->samples[i].ts ||
+		    !sample_send[i].ts ||
+		    !client->samples[i].ts ||
+		    server->samples[i].id != sample_send[i].id ||
+		    server->samples[i].id != client->samples[i].id) {
+			(*bad_probes)++;
+			continue;
+		}
+
+		ts = (sample_send[i].ts + server->samples[i].ts) / 2;
+		offset = client->samples[i].ts - ts;
+
+		delta = server->samples[i].ts - sample_send[i].ts;
+		if (!delta_min || delta_min > delta) {
+			delta_min = delta;
+			*offset_ret = offset;
+			*ts_ret = ts;
+		}
+#ifdef TSYNC_DEBUG
+		{
+			struct ptp_clock_sync *ptp;
+
+			ptp = (struct ptp_clock_sync *)clock->proto_data;
+			if (ptp && ptp->debug_fd > 0) {
+				char buff[256];
+
+				sprintf(buff, "%lld %lld %lld\n",
+					ts, client->samples[i].ts, offset);
+				write(ptp->debug_fd, buff, strlen(buff));
+			}
+		}
+#endif
+	}
+
+	return 0;
+}
+
+#else /* histogram */
+
+static int ptp_calc_offset(struct clock_sync_context *clock,
+			   struct ptp_clock_result_msg *server,
+			   struct ptp_clock_result_msg *client,
+			   long long *offset_ret, long long *ts_ret,
+			   int *bad_probes)
+{
+	struct ptp_clock_sample *sample_send;
+	long long timestamps[PTP_SYNC_LOOP];
+	long long offsets[PTP_SYNC_LOOP];
+	int hist[PTP_SYNC_LOOP];
+	long long offset_max = 0;
+	long long offset_min = 0;
+	int ind, max = 0;
+	long long bin;
+	int i, k = 0;
+
+	*bad_probes = 0;
+	memset(hist, 0, sizeof(int) * PTP_SYNC_LOOP);
+	sample_send = server->samples + (server->count / 2);
+	for (i = 0; i * 2 < server->count && i < client->count; i++) {
+		if (!server->samples[i].ts ||
+		    !sample_send[i].ts ||
+		    !client->samples[i].ts ||
+		    server->samples[i].id != sample_send[i].id ||
+		    server->samples[i].id != client->samples[i].id) {
+			(*bad_probes)++;
+			continue;
+		}
+
+		timestamps[k] = (sample_send[i].ts + server->samples[i].ts) / 2;
+		offsets[k] = client->samples[i].ts - timestamps[k];
+		if (!offset_max || offset_max < llabs(offsets[k]))
+			offset_max = llabs(offsets[k]);
+		if (!offset_min || offset_min > llabs(offsets[k]))
+			offset_min = llabs(offsets[k]);
+#ifdef TSYNC_DEBUG
+		{
+			struct ptp_clock_sync *ptp;
+
+			ptp = (struct ptp_clock_sync *)clock->proto_data;
+
+			if (ptp && ptp->debug_fd > 0) {
+				char buff[256];
+
+				sprintf(buff, "%lld %lld %lld\n",
+					timestamps[k],
+					client->samples[i].ts, offsets[k]);
+				write(ptp->debug_fd, buff, strlen(buff));
+			}
+		}
+#endif
+		k++;
+	}
+
+	bin = (offset_max - offset_min) / PTP_SYNC_LOOP;
+	for (i = 0; i < k; i++) {
+		ind = (llabs(offsets[i]) - offset_min) / bin;
+		if (ind < PTP_SYNC_LOOP) {
+			hist[ind]++;
+			if (max < hist[ind]) {
+				max = hist[ind];
+				*offset_ret = offsets[i];
+				*ts_ret = timestamps[i];
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static void ntoh_ptp_results(struct ptp_clock_result_msg *msg)
+{
+	int i;
+
+	msg->count = ntohl(msg->count);
+	for (i = 0; i < msg->count; i++) {
+		msg->samples[i].id = ntohl(msg->samples[i].id);
+		msg->samples[i].ts = ntohll(msg->samples[i].ts);
+	}
+	msg->series_id = ntohl(msg->series_id);
+}
+
+
+static void hton_ptp_results(struct ptp_clock_result_msg *msg)
+{
+	int i;
+
+	for (i = 0; i < msg->count; i++) {
+		msg->samples[i].id = htonl(msg->samples[i].id);
+		msg->samples[i].ts = htonll(msg->samples[i].ts);
+	}
+	msg->series_id = htonl(msg->series_id);
+	msg->count = htonl(msg->count);
+}
+
+static int ptp_clock_client(struct tracecmd_time_sync *tsync,
+			    long long *offset, long long *timestamp)
+{
+	struct clock_sync_context *clock_context;
+	struct ptp_clock_offset_msg res_offset;
+	unsigned int sync_proto, sync_msg;
+	struct ptp_clock_start_msg start;
+	struct ptp_markers_context ctx;
+	struct ptp_clock_sync *ptp;
+	struct ptp_marker marker;
+	unsigned int size;
+	char *msg;
+	int count;
+	int ret;
+
+	if (!tsync || !tsync->context || !tsync->msg_handle)
+		return -1;
+
+	clock_context = (struct clock_sync_context *)tsync->context;
+	if (clock_context->proto_data == NULL)
+		return -1;
+
+	ptp = (struct ptp_clock_sync *)clock_context->proto_data;
+	size = sizeof(start);
+	msg = (char *)&start;
+	ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+					  &sync_proto, &sync_msg,
+					  &size, &msg);
+	if (ret || sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+	    sync_msg != PTP_SYNC_PKT_START)
+		return -1;
+	ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+					  TRACECMD_TIME_SYNC_PROTO_PTP,
+					  PTP_SYNC_PKT_START, sizeof(start),
+					  (char *)&start);
+	marker.data.local_cid = clock_context->local_cid;
+	marker.data.remote_cid = clock_context->remote_cid;
+	marker.series_id = ntohl(start.series_id);
+	marker.data.packet_id = 'r';
+	ptp->series_id = marker.series_id;
+	msg = (char *)&count;
+	size = sizeof(count);
+	while (true) {
+		count = 0;
+		ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+						  &sync_proto, &sync_msg,
+						  &size, &msg);
+		if (ret || sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+		    sync_msg != PTP_SYNC_PKT_PROBE || !ntohl(count))
+			break;
+
+		marker.data.count = ntohl(count);
+		write(ptp->marker_fd, &marker, sizeof(marker));
+		ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+						 TRACECMD_TIME_SYNC_PROTO_PTP,
+						 PTP_SYNC_PKT_PROBE,
+						 sizeof(count), (char *)&count);
+		if (ret)
+			break;
+	}
+
+	if (sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+	    sync_msg != PTP_SYNC_PKT_END)
+		return -1;
+
+	ctx.size = PTP_SYNC_LOOP;
+	ctx.ptp = ptp;
+	ctx.clock = clock_context;
+	ctx.msg.count = 0;
+	ctx.msg.series_id = ptp->series_id;
+	tracecmd_iterate_raw_events(ptp->tep, clock_context->vinst,
+				    ptp_marker_find, &ctx);
+	hton_ptp_results(&ctx.msg);
+	ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+					  TRACECMD_TIME_SYNC_PROTO_PTP,
+					  PTP_SYNC_PKT_PROBES,
+					  sizeof(ctx.msg), (char *)&ctx.msg);
+
+	msg = (char *)&res_offset;
+	size = sizeof(res_offset);
+	ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+					  &sync_proto, &sync_msg,
+					  &size, (char **)&msg);
+	if (ret || sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+	    sync_msg != PTP_SYNC_PKT_OFFSET)
+		return -1;
+
+	*offset = ntohll(res_offset.offset);
+	*timestamp = ntohll(res_offset.ts);
+
+	return 0;
+}
+
+
+static int ptp_clock_server(struct tracecmd_time_sync *tsync,
+			    long long *offset, long long *timestamp)
+{
+	struct ptp_clock_result_msg *results = NULL;
+	struct clock_sync_context *clock_context;
+	struct ptp_clock_offset_msg res_offset;
+	unsigned int sync_proto, sync_msg;
+	struct ptp_clock_start_msg start;
+	struct ptp_markers_context ctx;
+	int sync_loop = PTP_SYNC_LOOP;
+	struct ptp_clock_sync *ptp;
+	struct ptp_marker marker;
+	unsigned int size;
+	int bad_probes;
+	int count = 1;
+	int msg_count;
+	int msg_ret;
+	char *msg;
+	int ret;
+
+	if (!tsync || !tsync->context || !tsync->msg_handle)
+		return -1;
+
+	clock_context = (struct clock_sync_context *)tsync->context;
+	if (clock_context->proto_data == NULL)
+		return -1;
+
+	ptp = (struct ptp_clock_sync *)clock_context->proto_data;
+	start.series_id = htonl(ptp->series_id + 1);
+	ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+					 TRACECMD_TIME_SYNC_PROTO_PTP,
+					 PTP_SYNC_PKT_START, sizeof(start),
+					 (char *)&start);
+	if (!ret)
+		ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+						  &sync_proto, &sync_msg,
+						  NULL, NULL);
+	if (ret || sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+	    sync_msg != PTP_SYNC_PKT_START)
+		return -1;
+
+	tracecmd_write_instance_file(clock_context->vinst, "trace", "\0", NULL);
+
+	ptp->series_id++;
+	marker.data.local_cid = clock_context->local_cid;
+	marker.data.remote_cid = clock_context->remote_cid;
+	marker.series_id = ptp->series_id;
+	msg = (char *)&msg_ret;
+	size = sizeof(msg_ret);
+	do {
+		marker.data.count = count++;
+		marker.data.packet_id = 's';
+		msg_count = htonl(marker.data.count);
+		write(ptp->marker_fd, &marker, sizeof(marker));
+		ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+						 TRACECMD_TIME_SYNC_PROTO_PTP,
+						 PTP_SYNC_PKT_PROBE,
+						 sizeof(msg_count),
+						 (char *)&msg_count);
+		if (!ret)
+			ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+							  &sync_proto, &sync_msg,
+							  &size, &msg);
+
+		marker.data.packet_id = 'r';
+		write(ptp->marker_fd, &marker, sizeof(marker));
+		if (ret || sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+		    sync_msg != PTP_SYNC_PKT_PROBE ||
+		    ntohl(msg_ret) != marker.data.count)
+			break;
+	} while (--sync_loop);
+
+	if (sync_loop)
+		return -1;
+
+	ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+					  TRACECMD_TIME_SYNC_PROTO_PTP,
+					  PTP_SYNC_PKT_END, 0, NULL);
+
+	size = 0;
+	ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
+					  &sync_proto, &sync_msg,
+					  &size, (char **)&results);
+	if (ret || sync_proto != TRACECMD_TIME_SYNC_PROTO_PTP ||
+	    sync_msg != PTP_SYNC_PKT_PROBES || size == 0 || results == NULL)
+		return -1;
+
+	ntoh_ptp_results(results);
+
+	ctx.size = 2*PTP_SYNC_LOOP;
+	ctx.ptp = ptp;
+	ctx.clock = clock_context;
+	ctx.msg.count = 0;
+	ctx.msg.series_id = ptp->series_id;
+	tracecmd_iterate_raw_events(ptp->tep, clock_context->vinst,
+				    ptp_marker_find, &ctx);
+
+	ptp_calc_offset(clock_context, &ctx.msg, results, offset,
+			timestamp, &bad_probes);
+
+#ifdef TSYNC_DEBUG
+	{
+		char buff[256];
+		int res_fd;
+
+		sprintf(buff, "res-cid%d.txt", clock_context->remote_cid);
+
+		res_fd = open(buff, O_WRONLY|O_APPEND, 0644);
+		if (res_fd > 0) {
+			if (*offset && *timestamp) {
+				sprintf(buff, "%d %lld %lld\n",
+					ptp->series_id, *offset, *timestamp);
+				write(res_fd, buff, strlen(buff));
+			}
+			close(res_fd);
+		}
+
+		printf("\n calculated offset %d: %lld, %d probes, filtered out %d\n\r",
+			ptp->series_id, *offset, results->count, bad_probes);
+		if (ptp && ptp->debug_fd > 0) {
+			sprintf(buff, "%lld %lld 0\n", *offset, *timestamp);
+			write(ptp->debug_fd, buff, strlen(buff));
+			close(ptp->debug_fd);
+			ptp->debug_fd = -1;
+		}
+
+	}
+#endif
+
+	res_offset.offset = htonll(*offset);
+	res_offset.ts = htonll(*timestamp);
+	ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
+					  TRACECMD_TIME_SYNC_PROTO_PTP,
+					  PTP_SYNC_PKT_OFFSET,
+					  sizeof(res_offset),
+					  (char *)&res_offset);
+
+	free(results);
+	return 0;
+}
+
+static int ptp_clock_sync_calc(struct tracecmd_time_sync *tsync,
+			       long long *offset, long long *timestamp)
+{
+	struct clock_sync_context *clock_context;
+	int ret;
+
+	if (!tsync || !tsync->context)
+		return -1;
+	clock_context = (struct clock_sync_context *)tsync->context;
+
+#ifdef TSYNC_DEBUG
+	if (clock_context->server) {
+		struct ptp_clock_sync *ptp;
+		char buff[256];
+
+		ptp = (struct ptp_clock_sync *)clock_context->proto_data;
+		if (ptp->debug_fd > 0)
+			close(ptp->debug_fd);
+		sprintf(buff, "s-cid%d_%d.txt",
+				clock_context->remote_cid, ptp->series_id+1);
+		ptp->debug_fd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644);
+	}
+#endif
+
+
+	if (clock_context->server)
+		ret = ptp_clock_server(tsync, offset, timestamp);
+	else
+		ret = ptp_clock_client(tsync, offset, timestamp);
+
+	return ret;
+}
+
+int ptp_clock_sync_register(void)
+{
+	return tracecmd_tsync_proto_register(TRACECMD_TIME_SYNC_PROTO_PTP,
+					     TRACECMD_TIME_SYNC_PROTO_PTP_WEIGHT,
+					     ptp_clock_sync_init,
+					     ptp_clock_sync_free,
+					     ptp_clock_sync_calc);
+
+}
+
+int ptp_clock_sync_unregister(void)
+{
+	return tracecmd_tsync_proto_unregister(TRACECMD_TIME_SYNC_PROTO_PTP);
+}
diff --git a/lib/trace-cmd/trace-timesync.c b/lib/trace-cmd/trace-timesync.c
index 073423d..5458c4f 100644
--- a/lib/trace-cmd/trace-timesync.c
+++ b/lib/trace-cmd/trace-timesync.c
@@ -42,6 +42,14 @@ static struct tsync_proto *tsync_proto_get(unsigned int proto_id)
 	return NULL;
 }
 
+/**
+ * tracecmd_tsync_init - Initialize the global, per task, time sync data.
+ */
+void tracecmd_tsync_init(void)
+{
+	ptp_clock_sync_register();
+}
+
 int tracecmd_tsync_proto_register(unsigned int proto_id, int weight,
 				int (*init)(struct tracecmd_time_sync *),
 				int (*free)(struct tracecmd_time_sync *),
diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
index 1581de8..46874cf 100644
--- a/tracecmd/trace-agent.c
+++ b/tracecmd/trace-agent.c
@@ -242,6 +242,8 @@ static void agent_serve(unsigned int port)
 	if (sd < 0)
 		die("Failed to open vsocket");
 
+	tracecmd_tsync_init();
+
 	if (!get_local_cid(&cid))
 		printf("listening on @%u:%u\n", cid, port);
 
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index e543c40..c29f1ce 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -5944,10 +5944,7 @@ static void set_clock(struct buffer_instance *instance)
 		tracecmd_put_tracing_file(path);
 	}
 }
-/*
- * This function contains common code for the following commands:
- * record, start, stream, profile.
- */
+
 static void record_trace(int argc, char **argv,
 			 struct common_record_context *ctx)
 {
@@ -6080,12 +6077,24 @@ static void record_trace(int argc, char **argv,
 	finalize_record_trace(ctx);
 }
 
+/*
+ * This function contains common code for the following commands:
+ * record, start, stream, profile.
+ */
+static void record_trace_command(int argc, char **argv,
+				 struct common_record_context *ctx)
+{
+	tracecmd_tsync_init();
+	record_trace(argc, argv, ctx);
+}
+
+
 void trace_start(int argc, char **argv)
 {
 	struct common_record_context ctx;
 
 	parse_record_options(argc, argv, CMD_start, &ctx);
-	record_trace(argc, argv, &ctx);
+	record_trace_command(argc, argv, &ctx);
 	exit(0);
 }
 
@@ -6165,7 +6174,7 @@ void trace_stream(int argc, char **argv)
 	struct common_record_context ctx;
 
 	parse_record_options(argc, argv, CMD_stream, &ctx);
-	record_trace(argc, argv, &ctx);
+	record_trace_command(argc, argv, &ctx);
 	exit(0);
 }
 
@@ -6184,7 +6193,7 @@ void trace_profile(int argc, char **argv)
 	if (!buffer_instances)
 		top_instance.flags |= BUFFER_FL_PROFILE;
 
-	record_trace(argc, argv, &ctx);
+	record_trace_command(argc, argv, &ctx);
 	do_trace_profile();
 	exit(0);
 }
@@ -6203,7 +6212,7 @@ void trace_record(int argc, char **argv)
 	struct common_record_context ctx;
 
 	parse_record_options(argc, argv, CMD_record, &ctx);
-	record_trace(argc, argv, &ctx);
+	record_trace_command(argc, argv, &ctx);
 	exit(0);
 }
 
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v17 18/18] trace-cmd: Debug scripts for PTP-like algorithm for host - guest timestamp synchronization
  2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
                   ` (16 preceding siblings ...)
  2019-12-03 10:35 ` [PATCH v17 17/18] trace-cmd: [POC] PTP-like algorithm " Tzvetomir Stoyanov (VMware)
@ 2019-12-03 10:35 ` Tzvetomir Stoyanov (VMware)
  17 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2019-12-03 10:35 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

These scripts can be used to visualise debug files, written when the PTP-like algorithm
is compiled with TSYNC_DEBUG defined. The files are located in the guest machine:
    s-cid*.txt - For each offset calculation: host and guest clocks and calculated offset.
    res-cid*.txt - For each tracing session: all calculated clock offsets.

tsync_hist.py plots a histogram, using data from a s-cid*.txt file:
	"python tsync_hist.py s-cid2_1.txt"
tsync_res.py plots a line, using data from res-cid*.txt file:
	"python tsync_res.py res-cid2.txt"

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 scripts/debug/tsync_hist.py | 57 +++++++++++++++++++++++++++++++++++++
 scripts/debug/tsync_readme  | 12 ++++++++
 scripts/debug/tsync_res.py  | 46 ++++++++++++++++++++++++++++++
 3 files changed, 115 insertions(+)
 create mode 100644 scripts/debug/tsync_hist.py
 create mode 100644 scripts/debug/tsync_readme
 create mode 100644 scripts/debug/tsync_res.py

diff --git a/scripts/debug/tsync_hist.py b/scripts/debug/tsync_hist.py
new file mode 100644
index 0000000..819d1e8
--- /dev/null
+++ b/scripts/debug/tsync_hist.py
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2019, VMware Inc, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+# Copyright (C) 2019, VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+
+
+import matplotlib.pyplot as plt
+import matplotlib.lines as mlines
+import numpy as np
+import sys
+
+def newline(p1, p2):
+    ax = plt.gca()
+    xmin, xmax = ax.get_xbound()
+
+    if(p2[0] == p1[0]):
+        xmin = xmax = p1[0]
+        ymin, ymax = ax.get_ybound()
+    else:
+        ymax = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmax-p1[0])
+        ymin = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmin-p1[0])
+
+    l = mlines.Line2D([xmin,xmax], [ymin,ymax], color='red')
+    ax.add_line(l)
+    return l
+
+
+data = np.loadtxt(fname = sys.argv[1])
+selected_ts  = data[-1, 1]
+selected_ofs = data[-1, 0]
+data = data[:-1,:]
+
+x = data[:, 1] - data[:, 0]
+
+mean = x.mean()
+std = x.std()
+
+num_bins = 500
+min = x.min() #+ .4 * (x.max() - x.min())
+max = x.max() #- .4 * (x.max() - x.min())
+bins = np.linspace(min, max, num_bins, endpoint = False, dtype=int)
+
+fig, ax = plt.subplots()
+
+# the histogram of the data
+n, bins, patches = ax.hist(x, bins, histtype=u'step');
+
+ax.set_xlabel('clock offset [$\mu$s]')
+ax.set_ylabel('entries')
+ax.set_title("$\sigma$=%i" % std)
+
+x1, y1 = [selected_ofs, min], [selected_ofs, max]
+newline(x1, y1)
+
+# Tweak spacing to prevent clipping of ylabel
+fig.tight_layout()
+plt.show()
diff --git a/scripts/debug/tsync_readme b/scripts/debug/tsync_readme
new file mode 100644
index 0000000..f3ebb25
--- /dev/null
+++ b/scripts/debug/tsync_readme
@@ -0,0 +1,12 @@
+PTP-like algorithm debug
+========================
+
+tsync_*.py scripts can be used to visualise debug files, written when the PTP-like algorithm
+is compiled with TSYNC_DEBUG defined. The files are located in the guest machine:
+    s-cid*.txt - For each offset calculation: host and guest clocks and calculated offset.
+    res-cid*.txt - For each tracing session: all calculated clock offsets.
+
+tsync_hist.py plots a histogram, using data from a s-cid*.txt file:
+	"python tsync_hist.py s-cid2_1.txt"
+tsync_res.py plots a line, using data from res-cid*.txt file:
+	"python tsync_res.py res-cid2.txt"
diff --git a/scripts/debug/tsync_res.py b/scripts/debug/tsync_res.py
new file mode 100644
index 0000000..7d10986
--- /dev/null
+++ b/scripts/debug/tsync_res.py
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2019, VMware Inc, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+# Copyright (C) 2019, VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+
+
+import matplotlib.pyplot as plt
+import matplotlib.lines as mlines
+import numpy as np
+import sys
+
+def newline(p1, p2):
+    ax = plt.gca()
+    xmin, xmax = ax.get_xbound()
+
+    if(p2[0] == p1[0]):
+        xmin = xmax = p1[0]
+        ymin, ymax = ax.get_ybound()
+    else:
+        ymax = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmax-p1[0])
+        ymin = p1[1]+(p2[1]-p1[1])/(p2[0]-p1[0])*(xmin-p1[0])
+
+    l = mlines.Line2D([xmin,xmax], [ymin,ymax], color='red')
+    ax.add_line(l)
+    return l
+
+data = np.loadtxt(fname = sys.argv[1])
+x = data[:, 0]
+y = data[:, 1]
+
+fig, ax = plt.subplots()
+
+ax.set_xlabel('samples (t)')
+ax.set_ylabel('clock offset')
+ax.set_title("$\delta$=%i ns" % (max(y) - min(y)))
+
+l = mlines.Line2D(x, y)
+ax.add_line(l)
+ax.set_xlim(min(x), max(x))
+ax.set_ylim(min(y), max(y) )
+
+print(min(y), max(y), max(y) - min(y))
+
+# Tweak spacing to prevent clipping of ylabel
+fig.tight_layout()
+plt.show()
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances.
  2019-12-03 10:35 ` [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances Tzvetomir Stoyanov (VMware)
@ 2019-12-04 16:17   ` Steven Rostedt
  2019-12-05 14:40     ` Tzvetomir Stoyanov
  0 siblings, 1 reply; 42+ messages in thread
From: Steven Rostedt @ 2019-12-04 16:17 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:08 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> From: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> 
> In order to reuse the code, the functionality related to
> ftrace instances is moved from trace-cmd application to
> libtracecmd. The following new library APIs are introduced:
> 
> library structure, representing a ftrace instance:
> 	struct tracecmd_instance {
>         	char    *name;
> 	        char    *clock;
> 	};
> 
> APIs for creating and deleting ftrace instances:
> 	struct tracecmd_instance *tracecmd_create_instance(const char *name);

It looks to me that tracecmd_create_instance() allocates the instance,
and tracecmd_free_instance() frees the memory. Let's rename this to
tracecmd_alloc_instance(), otherwise it becomes ambiguous to actually
creating the instance on file. When I first saw this, I thought this
would create the instance

  e.g. mkdir /sys/kernel/tracing/instances/foo


Which looks to be what tracecmd_make_instance() does.

> 	void tracecmd_free_instance(struct tracecmd_instance *instance);
> 	int tracecmd_make_instance(struct tracecmd_instance *instance);
> 	void tracecmd_remove_instance(struct tracecmd_instance *instance);
> 
> APIs for reading and writing ftrace files, instance aware:
> 	char *tracecmd_get_instance_file(struct tracecmd_instance *instance, const char *file);
> 	char *tracecmd_get_instance_dir(struct tracecmd_instance *instance);
> 	int tracecmd_write_instance_file(struct tracecmd_instance *instance,
> 	                                 const char *file, const char *str,
>         	                         const char *type);
> 	int tracecmd_write_file(const char *file, const char *str, const char *type);
> 	char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
>         	                          char *file, int *psize);
> 
> API for setting ftrace clock, instance aware:
> 	void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock);
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>  include/trace-cmd/trace-cmd.h           |  24 ++
>  lib/trace-cmd/Makefile                  |   1 +
>  lib/trace-cmd/include/trace-cmd-local.h |  33 +--
>  lib/trace-cmd/trace-instance.c          | 265 ++++++++++++++++++
>  lib/trace-cmd/trace-util.c              |  71 ++++-
>  tracecmd/include/trace-local.h          |   6 +-
>  tracecmd/trace-list.c                   |   2 +-
>  tracecmd/trace-record.c                 | 341 +++++++-----------------
>  tracecmd/trace-show.c                   |   2 +
>  tracecmd/trace-stat.c                   |  20 +-
>  10 files changed, 472 insertions(+), 293 deletions(-)
>  create mode 100644 lib/trace-cmd/trace-instance.c
> 
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index 7f9cb73..5287d23 100644
> --- a/include/trace-cmd/trace-cmd.h
> +++ b/include/trace-cmd/trace-cmd.h
> @@ -356,6 +356,30 @@ int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  				 int *nr_cpus, int *page_size,
>  				 unsigned int **ports, bool *use_fifos);
> +/* --- ftrace instances --- */
> +
> +struct tracecmd_instance {
> +	char	*name;
> +	char	*clock;
> +};

I wonder if we should keep this as a private structure. That is, only
declare the struct in the public header, but define the structure in a
private one. This will help with keeping people from using it directly,
and allowing for updating the structure without having to update the
library version.


> +
> +struct tracecmd_instance *tracecmd_create_instance(const char *name);
> +void tracecmd_free_instance(struct tracecmd_instance *instance);
> +int tracecmd_make_instance(struct tracecmd_instance *instance);
> +void tracecmd_remove_instance(struct tracecmd_instance *instance);
> +char *
> +tracecmd_get_instance_file(struct tracecmd_instance *instance, const char *file);
> +char *tracecmd_get_instance_dir(struct tracecmd_instance *instance);
> +int tracecmd_write_instance_file(struct tracecmd_instance *instance,
> +				 const char *file, const char *str,
> +				 const char *type);
> +
> +int tracecmd_write_file(const char *file, const char *str, const char *type);
> +char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
> +				  char *file, int *psize);
> +
> +void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock);
> +
>  
>  /* --- Plugin handling --- */
>  extern struct tep_plugin_option trace_ftrace_options[];
> diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
> index 3b4b5aa..18c7013 100644
> --- a/lib/trace-cmd/Makefile
> +++ b/lib/trace-cmd/Makefile
> @@ -13,6 +13,7 @@ OBJS += trace-input.o
>  OBJS += trace-output.o
>  OBJS += trace-recorder.o
>  OBJS += trace-util.o
> +OBJS += trace-instance.o
>  OBJS += trace-filter-hash.o
>  OBJS += trace-msg.o
>  
> diff --git a/lib/trace-cmd/include/trace-cmd-local.h b/lib/trace-cmd/include/trace-cmd-local.h
> index 09574db..eef4d39 100644
> --- a/lib/trace-cmd/include/trace-cmd-local.h
> +++ b/lib/trace-cmd/include/trace-cmd-local.h
> @@ -18,36 +18,7 @@
>  #define STR(x)	_STR(x)
>  #define FILE_VERSION_STRING STR(FILE_VERSION)
>  
> -static ssize_t __do_write(int fd, const void *data, size_t size)
> -{
> -	ssize_t tot = 0;
> -	ssize_t w;
> -
> -	do {
> -		w = write(fd, data + tot, size - tot);
> -		tot += w;
> -
> -		if (!w)
> -			break;
> -		if (w < 0)
> -			return w;
> -	} while (tot != size);
> -
> -	return tot;
> -}
> -
> -static ssize_t
> -__do_write_check(int fd, const void *data, size_t size)
> -{
> -	ssize_t ret;
> -
> -	ret = __do_write(fd, data, size);
> -	if (ret < 0)
> -		return ret;
> -	if (ret != size)
> -		return -1;
> -
> -	return 0;
> -}
> +ssize_t __do_write_check(int fd, const void *data, size_t size);
> +void __noreturn die(const char *fmt, ...); /* Can be overriden */
>  
>  #endif /* _TRACE_CMD_LOCAL_H */
> diff --git a/lib/trace-cmd/trace-instance.c b/lib/trace-cmd/trace-instance.c
> new file mode 100644
> index 0000000..1175b8c
> --- /dev/null
> +++ b/lib/trace-cmd/trace-instance.c
> @@ -0,0 +1,265 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +/*
> + * Copyright (C) 2019, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
> + *
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <time.h>
> +#include <poll.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <sys/stat.h>
> +
> +#include "trace-cmd.h"
> +#include "trace-cmd-local.h"
> +
> +/**
> + * tracecmd_put_tracing_file - Free tracing file / dir, created by
> + *		tracecmd_get_instance_dir() or tracecmd_get_instance_file()
> + *		APIs.
> + *@name: The name of the tracing file or dir
> + */
> +void tracecmd_put_tracing_file(char *name)
> +{
> +	free(name);
> +}
> +
> +/**
> + * tracecmd_create_instance - allocate a new ftrace instance
> + * @name: The name of the instance (instance will point to this)
> + *
> + * Returns a newly allocated instance. Note that @name will not be
> + * copied, and the instance buffer will point to the string itself.
> + */
> +struct tracecmd_instance *tracecmd_create_instance(const char *name)

Again, better name would be "_alloc_instance"

> +{
> +	struct tracecmd_instance *instance;
> +
> +	instance = malloc(sizeof(*instance));
> +	if (!instance)
> +		return NULL;
> +	memset(instance, 0, sizeof(*instance));
> +	if (name)
> +		instance->name = strdup(name);
> +
> +	return instance;
> +}
> +
> +/**
> + * tracecmd_free_instance - Free an instance struct, previously allocated by
> + *			    tracecmd_create_instance().
> + *@instance: Pointer to the instance to be freed
> + *
> + */
> +void tracecmd_free_instance(struct tracecmd_instance *instance)
> +{
> +	if (!instance)
> +		return;
> +
> +	free(instance->name);
> +	free(instance);
> +}
> +
> +/**
> + * tracecmd_make_instance - Create a new ftrace instance
> + * @instance: Pointer to the instance to be created
> + *
> + * Returns -1 in case of an erro, or 0 otherwise.
> + */
> +int tracecmd_make_instance(struct tracecmd_instance *instance)
> +{
> +	struct stat st;
> +	char *path;
> +	int ret;
> +
> +	path = tracecmd_get_instance_dir(instance);
> +	ret = stat(path, &st);
> +	if (ret < 0) {
> +		ret = mkdir(path, 0777);
> +		if (ret < 0)
> +			return ret;
> +
> +	} else
> +		ret = 1;
> +	tracecmd_put_tracing_file(path);
> +	return ret;
> +}
> +
> +/**
> + * tracecmd_remove_instance - Remove a ftrace instance
> + * @instance: Pointer to the instance to be removed
> + *
> + */
> +void tracecmd_remove_instance(struct tracecmd_instance *instance)

Should return an int.

> +{
> +	char *path;
> +
> +	path = tracecmd_get_instance_dir(instance);
> +	rmdir(path);

We should check the return status of rmdir, and return that.

> +	tracecmd_put_tracing_file(path);
> +}
> +
> +/**
> + * tracecmd_get_instance_file - return the path to a instance file.

Note, none of the one line descriptions should end in a period. There's
a few sprinkled around.

> + * @instance: buffer instance for the file, can be NULL for the top instance
> + * @file: name of file to return
> + *
> + * Returns the path name of the @file for the given @instance.
> + *
> + * Must use tracecmd_put_tracing_file() to free the returned string.
> + */
> +char *
> +tracecmd_get_instance_file(struct tracecmd_instance *instance, const char *file)
> +{
> +	char *path;
> +	char *buf;
> +	int ret;
> +
> +	if (instance && instance->name) {
> +		ret = asprintf(&buf, "instances/%s/%s", instance->name, file);
> +		if (ret < 0)
> +			die("Failed to allocate name for %s/%s", instance->name, file);
> +		path = tracecmd_get_tracing_file(buf);
> +		free(buf);
> +	} else
> +		path = tracecmd_get_tracing_file(file);
> +
> +	return path;
> +}
> +
> +/**
> + * tracecmd_get_instance_file - return the path to a instance file.
> + * @instance: buffer instance for the file, can be NULL for the top instance
> + * @file: name of file to return
> + *
> + * Returns the path name of the @file for the given @instance.
> + *
> + * Must use tracecmd_put_tracing_file() to free the returned string.
> + */
> +char *tracecmd_get_instance_dir(struct tracecmd_instance *instance)
> +{
> +	char *buf;
> +	char *path;
> +	int ret;
> +
> +	if (instance->name) {
> +		ret = asprintf(&buf, "instances/%s", instance->name);
> +		if (ret < 0)
> +			die("Failed to allocate for instance %s", instance->name);
> +		path = tracecmd_get_tracing_file(buf);
> +		free(buf);
> +	} else
> +		path = tracecmd_find_tracing_dir();
> +
> +	return path;
> +}
> +
> +/**
> + * tracecmd_write_instance_file - Write in trace file of specific instance.
> + * @instance: buffer instance for the file, can be NULL for the top instance
> + * @file: name of the file
> + * @str: Null terminated string, that will be written in the file.
> + * @type: Null terminated string, describing the current write operation.

BTW, the correct term is "nul terminated string", as NULL is a pointer,
and "nul" is '\0'.

> + *	  Used for logging purposes.
> + *
> + * Returns the number of written bytes, or -1 in case of an error
> + */
> +int tracecmd_write_instance_file(struct tracecmd_instance *instance,
> +				 const char *file, const char *str,
> +				 const char *type)
> +{
> +	struct stat st;
> +	char *path;
> +	int ret;
> +
> +	path = tracecmd_get_instance_file(instance, file);
> +	ret = stat(path, &st);
> +	if (ret == 0)
> +		ret = tracecmd_write_file(path, str, type);
> +	tracecmd_put_tracing_file(path);
> +
> +	return ret;
> +}
> +
> +/**
> + * tracecmd_read_instance_file - Read from a trace file of specific instance.
> + * @instance: buffer instance for the file, can be NULL for the top instance
> + * @file: name of the file
> + * @psize: Returns the number of bytes read.
> + *
> + * Returns a pointer to a NULL terminated string, read from the file, or NULL in

Again, 'a nul terminated string'

> + * case of an error.

We should also state that the returned string needs to be freed via
free().

> + */
> +char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
> +				  char *file, int *psize)
> +{
> +	char buffer[BUFSIZ];
> +	int size = 0;
> +	char *path;
> +	char *buf;
> +	int fd;
> +	int r;
> +
> +	path = tracecmd_get_instance_file(instance, file);
> +	fd = open(path, O_RDONLY);
> +	tracecmd_put_tracing_file(path);
> +	if (fd < 0) {
> +		warning("File %s not found", file);
> +		return NULL;
> +	}

Probably should add an empty line here.

> +	do {
> +		r = read(fd, buffer, BUFSIZ);
> +		if (r <= 0)
> +			continue;
> +		if (size)
> +			buf = realloc(buf, size+r+1);
> +		else
> +			buf = malloc(r+1);

I know this is only cut and pasted from what the original was, but we
should probably (either in this patch, or perhaps a separate one) fix
the above. As realloc(NULL, x) is equivalent to malloc(x), we can just
have:

		buf = realloc(buf, size+r+1);


> +		if (!buf)
> +			die("Failed to allocate instance file buffer");

As this is becoming a library function, we should remove "die()", as
that is only allowed in the application. Not library calls.

That would also mean we need to clean up anything we allocated on
failure, and that the realloc would need to be:

		new_buf = realloc(buf, size + r + 1);

		if (!new_buf) {
			free(buf);
			return NULL;

All the functions that are being moved into the library needs to have
their die() calls removed. This is OK to do in a separate patch.

Preferably right after this patch (so reviewers know it's happening).


> +		memcpy(buf+size, buffer, r);
> +		size += r;
> +	} while (r);
> +
> +	buf[size] = '\0';
> +	if (psize)
> +		*psize = size;
> +	return buf;
> +}
> +
> +/**
> + * tracecmd_set_clock - Set the clock of ftrace event's timestamps, per instance.
> + * @instance: Pointer to ftrace instance, containing the desired clock.
> + * @old_clock: Optional, return the newly allocated string with the old clock.

This looks to be just a copy of the old code and forced into a library
function. As a library function, it should return an "int" and be
passed the clock name directly. Not via the instance->clock.

-- Steve

> + *
> + */
> +void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock)
> +{
> +	char *content;
> +	char *str;
> +
> +	if (!instance->clock)
> +		return;
> +
> +	/* The current clock is in brackets, reset it when we are done */
> +	content = tracecmd_read_instance_file(instance, "trace_clock", NULL);
> +
> +	/* check if first clock is set */
> +	if (*content == '[')
> +		str = strtok(content+1, "]");
> +	else {
> +		str = strtok(content, "[");
> +		if (!str)
> +			die("Can not find clock in trace_clock");
> +		str = strtok(NULL, "]");
> +	}
> +	if (old_clock)
> +		*old_clock = strdup(str);
> +
> +	free(content);
> +	tracecmd_write_instance_file(instance,
> +				     "trace_clock", instance->clock, "clock");
> +}
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 05/18] trace-cmd: Add new library API for local CPU count
  2019-12-03 10:35 ` [PATCH v17 05/18] trace-cmd: Add new library API for local CPU count Tzvetomir Stoyanov (VMware)
@ 2019-12-04 20:09   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-04 20:09 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:09 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> In order to reuse the code, the function detecting the
> number of local CPU is moved from trace-cmd application to
> libtracecmd. The following new library API is introduced:
> 	int tracecmd_count_cpus(void);
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>  include/trace-cmd/trace-cmd.h  |  3 +++
>  lib/trace-cmd/trace-util.c     | 40 +++++++++++++++++++++++++++++
>  tracecmd/include/trace-local.h |  2 --
>  tracecmd/trace-agent.c         |  2 +-
>  tracecmd/trace-profile.c       |  2 +-
>  tracecmd/trace-record.c        | 46 +++-------------------------------
>  tracecmd/trace-stat.c          |  4 +--
>  7 files changed, 50 insertions(+), 49 deletions(-)
> 
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index 5287d23..5d4292e 100644
> --- a/include/trace-cmd/trace-cmd.h
> +++ b/include/trace-cmd/trace-cmd.h
> @@ -427,6 +427,9 @@ void tracecmd_plog(const char *fmt, ...);
>  void tracecmd_plog_error(const char *fmt, ...);
>  int tracecmd_set_logfile(char *logfile);
>  
> +/* --- System --- */
> +int tracecmd_count_cpus(void);
> +
>  /* --- Hack! --- */
>  int tracecmd_blk_hack(struct tracecmd_input *handle);
>  
> diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
> index 4c70d9a..e019dce 100644
> --- a/lib/trace-cmd/trace-util.c
> +++ b/lib/trace-cmd/trace-util.c
> @@ -1112,6 +1112,46 @@ int tracecmd_stack_tracer_status(int *status)
>  	return 1; /* full success */
>  }
>  
> +int tracecmd_count_cpus(void)
> +{
> +	FILE *fp;
> +	char buf[1024];
> +	int cpus = 0;
> +	char *pbuf;
> +	size_t *pn;
> +	size_t n;
> +	int r;
> +
> +	cpus = sysconf(_SC_NPROCESSORS_CONF);
> +	if (cpus > 0)
> +		return cpus;
> +
> +	warning("sysconf could not determine number of CPUS");

As this is now a library function, probably should have a static
variable here:

	static int once;

	if (!once) {
		once++;
		warning("...");
	}

No need to do more than one warning.


> +
> +	/* Do the hack to figure out # of CPUS */
> +	n = 1024;
> +	pn = &n;
> +	pbuf = buf;
> +
> +	fp = fopen("/proc/cpuinfo", "r");
> +	if (!fp)
> +		die("Can not read cpuinfo");

Need to get rid of the die(). Could be in a separate patch.

Will also need to have the call sites check to see if the returned
number is negative, and fail there.

-- Steve

> +
> +	while ((r = getline(&pbuf, pn, fp)) >= 0) {
> +		char *p;
> +
> +		if (strncmp(buf, "processor", 9) != 0)
> +			continue;
> +		for (p = buf+9; isspace(*p); p++)
> +			;
> +		if (*p == ':')
> +			cpus++;
> +	}
> +	fclose(fp);
> +
> +	return cpus;
> +}
> +
>  /*
>   * tracecmd_write_file - Write in trace file
>   * @file: Full name of the trace file.
> diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 06/18] trace-cmd: Add new library API for reading ftrace buffers
  2019-12-03 10:35 ` [PATCH v17 06/18] trace-cmd: Add new library API for reading ftrace buffers Tzvetomir Stoyanov (VMware)
@ 2019-12-04 21:10   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-04 21:10 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:10 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> A new libtracecmd API is introduced:
> 
> 	int tracecmd_iterate_raw_events(struct tep_handle *tep,
> 					struct tracecmd_instance *instance,
> 					int (*callback)(struct tep_event *,
> 						   	struct tep_record *,
> 						   	int, void *context),
> 					void *callback_context);
> 
> It reads events from trace_pipe_raw, per cpu ftrace buffer, and calls a user
> callback for each of them. The API is instance aware.

Hmm, would be good to know what this would be used for.

> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>  include/trace-cmd/trace-cmd.h |   8 +++
>  lib/trace-cmd/trace-util.c    | 114 ++++++++++++++++++++++++++++++++++
>  2 files changed, 122 insertions(+)
> 
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index 5d4292e..831bb1a 100644
> --- a/include/trace-cmd/trace-cmd.h
> +++ b/include/trace-cmd/trace-cmd.h
> @@ -380,6 +380,14 @@ char *tracecmd_read_instance_file(struct tracecmd_instance *instance,
>  
>  void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock);
>  
> +int tracecmd_iterate_raw_events(struct tep_handle *tep,
> +				struct tracecmd_instance *instance,
> +				   int (*callback)(struct tep_event *,
> +						   struct tep_record *,
> +						   int, void *context),
> +				void *callback_context);
> +
> +
>  
>  /* --- Plugin handling --- */
>  extern struct tep_plugin_option trace_ftrace_options[];
> diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
> index e019dce..a54a905 100644
> --- a/lib/trace-cmd/trace-util.c
> +++ b/lib/trace-cmd/trace-util.c
> @@ -1185,3 +1185,117 @@ int tracecmd_write_file(const char *file, const char *str, const char *type)
>  	}
>  	return ret;
>  }
> +
> +static int
> +get_events_in_page(struct tep_handle *tep, void *page,
> +		   int size, int cpu,
> +		   int (*callback)(struct tep_event *,
> +				   struct tep_record *,
> +				   int, void *),
> +		   void *callback_context)
> +{
> +	struct tep_record *last_record = NULL;
> +	struct tep_event *event = NULL;
> +	struct tep_record *record;
> +	int id, cnt = 0;
> +
> +	if (size <= 0)
> +		return 0;
> +
> +	while (true) {
> +		event = NULL;
> +		record = tracecmd_read_page_record(tep, page, size,
> +						   last_record);
> +		if (!record)
> +			break;
> +		free_record(last_record);
> +		id = tep_data_type(tep, record);
> +		event = tep_find_event(tep, id);
> +		if (event && callback) {
> +			if (callback(event, record, cpu, callback_context))

Hmm, need to be documented that breaking out of the loop will likely
call future reads to lose events. The records on the page after the
record that causes the break will be lost from future reads.

-- Steve



> +				break;
> +		}
> +		last_record = record;
> +	}
> +	free_record(last_record);
> +
> +	return cnt;
> +}
> +
> +/*
> + * tracecmd_iterate_raw_events - Iterate through events in trace_pipe_raw
> + *				 per CPU trace files
> + * @tep: a handle to the trace event parser context
> + * @instance: ftrace instance, can be NULL for the top instance
> + * @callback: A user function, called for each record from the file.
> + * @callback_context: A custom context, passed to the user callback function
> + *
> + * If the @callback returns non-zero, the iteration stops.
> + *
> + * Returns -1 in case of an error, or 0 otherwise.
> + */
> +int tracecmd_iterate_raw_events(struct tep_handle *tep,
> +				struct tracecmd_instance *instance,
> +				int (*callback)(struct tep_event *,
> +						struct tep_record *,
> +						int, void *),
> +				void *callback_context)
> +{
> +	unsigned int p_size;
> +	struct dirent *dent;
> +	char file[PATH_MAX];
> +	void *page = NULL;
> +	struct stat st;
> +	char *path;
> +	DIR *dir;
> +	int ret;
> +	int cpu;
> +	int fd;
> +	int r;
> +
> +	p_size = getpagesize();
> +	path = tracecmd_get_instance_file(instance, "per_cpu");
> +	if (!path)
> +		return -1;
> +	dir = opendir(path);
> +	if (!dir) {
> +		ret = -1;
> +		goto error;
> +	}
> +	page = malloc(p_size);
> +	if (!page) {
> +		ret = -1;
> +		goto error;
> +	}
> +	while ((dent = readdir(dir))) {
> +		const char *name = dent->d_name;
> +
> +		if (strlen(name) < 4 || strncmp(name, "cpu", 3) != 0)
> +			continue;
> +		cpu = atoi(name + 3);
> +		sprintf(file, "%s/%s", path, name);
> +		ret = stat(file, &st);
> +		if (ret < 0 || !S_ISDIR(st.st_mode))
> +			continue;
> +
> +		sprintf(file, "%s/%s/trace_pipe_raw", path, name);
> +		fd = open(file, O_RDONLY | O_NONBLOCK);
> +		if (fd < 0)
> +			continue;
> +		do {
> +			r = read(fd, page, p_size);
> +			if (r > 0)
> +				get_events_in_page(tep, page, r, cpu,
> +						   callback, callback_context);
> +		} while (r > 0);
> +		close(fd);
> +	}
> +	ret = 0;
> +
> +error:
> +	if (dir)
> +		closedir(dir);
> +	free(page);
> +	tracecmd_put_tracing_file(path);
> +	return ret;
> +}


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 07/18] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM
  2019-12-03 10:35 ` [PATCH v17 07/18] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov (VMware)
@ 2019-12-04 21:35   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-04 21:35 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:11 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> From: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> 
> In order to match host and guest events, a mapping between guest VCPU
> and the host task, running this VCPU is needed. Extended existing
> struct guest to hold such mapping and added logic in read_qemu_guests()
> function to initialize it. Implemented a new internal API,
> get_guest_vcpu_pid(), to retrieve VCPU-task mapping for given VM.
> 
> Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> ---
>  tracecmd/include/trace-local.h |  2 ++
>  tracecmd/trace-record.c        | 57 ++++++++++++++++++++++++++++++++++
>  2 files changed, 59 insertions(+)
> 
> diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
> index a51f0d0..51abef1 100644
> --- a/tracecmd/include/trace-local.h
> +++ b/tracecmd/include/trace-local.h
> @@ -243,6 +243,8 @@ void update_first_instance(struct buffer_instance *instance, int topt);
>  
>  void show_instance_file(struct buffer_instance *instance, const char *name);
>  
> +int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu);
> +
>  /* moved from trace-cmd.h */
>  void tracecmd_create_top_instance(char *name);
>  void tracecmd_remove_instances(void);
> diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
> index 4459c90..fd7ca82 100644
> --- a/tracecmd/trace-record.c
> +++ b/tracecmd/trace-record.c
> @@ -2873,10 +2873,12 @@ static bool is_digits(const char *s)
>  	return true;
>  }
>  
> +#define VCPUS_MAX 256
>  struct guest {
>  	char *name;
>  	int cid;
>  	int pid;
> +	int cpu_pid[VCPUS_MAX];
>  };
>  
>  static struct guest *guests;
> @@ -2894,6 +2896,46 @@ static char *get_qemu_guest_name(char *arg)
>  	return arg;
>  }
>  
> +static void read_qemu_guests_pids(char *guest_task, struct guest *guest)
> +{
> +	struct dirent *entry;
> +	char path[PATH_MAX];
> +	char *buf = NULL;
> +	size_t n = 0;
> +	int vcpu;
> +	DIR *dir;
> +	FILE *f;
> +
> +	snprintf(path, sizeof(path), "/proc/%s/task", guest_task);
> +	dir = opendir(path);
> +	if (!dir)
> +		return;
> +
> +	while ((entry = readdir(dir))) {
> +		if (!(entry->d_type == DT_DIR && is_digits(entry->d_name)))
> +			continue;
> +
> +		snprintf(path, sizeof(path), "/proc/%s/task/%s/comm",
> +			 guest_task, entry->d_name);
> +		f = fopen(path, "r");
> +		if (!f)
> +			continue;
> +		if (getline(&buf, &n, f) < 0)
> +			goto next;
> +		if (strncmp(buf, "CPU ", 4) != 0)
> +			goto next;
> +
> +		vcpu = atoi(buf+4);
> +		if (!(vcpu >= 0 && vcpu < VCPUS_MAX))
> +			goto next;
> +		guest->cpu_pid[vcpu] = atoi(entry->d_name);
> +
> +next:

I don't usually mind goto's but the above could be simply written as:


		if (getline(&buf, &n, f) >= 0 &&
		    strncmp(buf, "CPU ", 4) == 0) {
			vcpu = atoi(buf + 4);
			if (vcpu >= 0 && vcpu < VCPUS_MAX)
				guest->cpu_pid[vcpu] = atoi(entry->d_name);
		}


-- Steve

> +		fclose(f);
> +	}
> +	free(buf);
> +}
> +
>  static void read_qemu_guests(void)
>  {
>  	static bool initialized;
> @@ -2957,6 +2999,8 @@ static void read_qemu_guests(void)
>  		if (!is_qemu)
>  			goto next;
>  
> +		read_qemu_guests_pids(entry->d_name, &guest);
> +
>  		guests = realloc(guests, (guests_len + 1) * sizeof(*guests));
>  		if (!guests)
>  			die("Can not allocate guest buffer");
> @@ -3002,6 +3046,19 @@ static char *parse_guest_name(char *guest, int *cid, int *port)
>  	return guest;
>  }
>  
> +int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu)
> +{
> +	int i;
> +
> +	if (!guests || guest_vcpu >= VCPUS_MAX)
> +		return -1;
> +
> +	for (i = 0; i < guests_len; i++)
> +		if (guest_cid == guests[i].cid)
> +			return guests[i].cpu_pid[guest_vcpu];
> +	return -1;
> +}
> +
>  static void set_prio(int prio)
>  {
>  	struct sched_param sp;


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 08/18] trace-cmd: Implement new API tracecmd_add_option_v()
  2019-12-03 10:35 ` [PATCH v17 08/18] trace-cmd: Implement new API tracecmd_add_option_v() Tzvetomir Stoyanov (VMware)
@ 2019-12-04 21:47   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-04 21:47 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:12 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> @@ -1046,6 +1083,57 @@ int tracecmd_write_options(struct tracecmd_output *handle)
>  	return 0;
>  }
>  
> +int tracecmd_append_options(struct tracecmd_output *handle)
> +{
> +	struct tracecmd_option *options;
> +	unsigned short option;
> +	unsigned short endian2;
> +	unsigned int endian4;
> +	off_t offset;
> +	int r;
> +
> +	/* If already written, ignore */
> +	if (handle->options_written)
> +		return 0;
> +
> +	if (lseek64(handle->fd, 0, SEEK_END) == (off_t)-1)
> +		return -1;
> +	offset = lseek64(handle->fd, -2, SEEK_CUR);
> +	if (offset == (off_t)-1)
> +		return -1;
> +
> +	r = pread(handle->fd, &option, 2, offset);
> +	if (r != 2 || option != TRACECMD_OPTION_DONE)
> +		return -1;
> +
> +	list_for_each_entry(options, &handle->options, list) {
> +		endian2 = convert_endian_2(handle, options->id);
> +		if (do_write_check(handle, &endian2, 2))
> +			return -1;
> +
> +		endian4 = convert_endian_4(handle, options->size);
> +		if (do_write_check(handle, &endian4, 4))
> +			return -1;
> +
> +		/* Save the data location in case it needs to be updated */
> +		options->offset = lseek64(handle->fd, 0, SEEK_CUR);
> +
> +		if (do_write_check(handle, options->data,
> +				   options->size))
> +			return -1;
> +	}
> +
> +	option = TRACECMD_OPTION_DONE;
> +
> +	if (do_write_check(handle, &option, 2))
> +		return -1;
> +
> +	handle->options_written = 1;
> +	handle->options_written = 1;

I think I'm seeing double.

-- Steve

> +
> +	return 0;
> +}
> +
>  int tracecmd_update_option(struct tracecmd_output *handle,
>  			   struct tracecmd_option *option, int size,
>  			   const void *data)


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 11/18] trace-cmd: Exchange tracing IDs between host and guest
  2019-12-03 10:35 ` [PATCH v17 11/18] trace-cmd: Exchange tracing IDs between host and guest Tzvetomir Stoyanov (VMware)
@ 2019-12-04 22:03   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-04 22:03 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:15 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
> index 6203e0d..a6f79c5 100644
> --- a/tracecmd/include/trace-local.h
> +++ b/tracecmd/include/trace-local.h
> @@ -98,8 +98,9 @@ void trace_usage(int argc, char **argv);
>  
>  int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
>  		       int cpus, int *fds,
> -		       int argc, char **argv, bool use_fifos);
> -
> +		       int argc, char **argv,
> +		       bool use_fifos,
> +		       unsigned long long trace_id);

I'd keep the empty line between the prototype and the struct
declaration below.

-- Steve

>  struct hook_list;
>  
>  void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hooks,

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT
  2019-12-03 10:35 ` [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov (VMware)
@ 2019-12-05  0:46   ` Steven Rostedt
  2019-12-05 15:09     ` Tzvetomir Stoyanov
  0 siblings, 1 reply; 42+ messages in thread
From: Steven Rostedt @ 2019-12-05  0:46 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:16 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> From: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> 
> The TRACECMD_OPTION_TIME_SHIFT is used when synchronizing trace time stamps between
> two trace.dat files. It contains multiple long long (time, offset) pairs, describing
> time stamps _offset_, measured in the given local _time_. The content of the option
> buffer is:
>  8 bytes - long long integer, ID of the tracing session
>  4 bytes - integer, count of timestamp offsets
>  long long array of size _count_, local time in which the offset is measured
>  long long array of size _count_, offset of the time stamps
> 
> Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> ---
>  include/trace-cmd/trace-cmd.h |   6 ++
>  lib/trace-cmd/trace-input.c   | 170 +++++++++++++++++++++++++++++++++-
>  2 files changed, 174 insertions(+), 2 deletions(-)
> 
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index 90fa146..1240b4a 100644
> --- a/include/trace-cmd/trace-cmd.h
> +++ b/include/trace-cmd/trace-cmd.h
> @@ -87,6 +87,7 @@ enum {
>  	TRACECMD_OPTION_VERSION,
>  	TRACECMD_OPTION_PROCMAPS,
>  	TRACECMD_OPTION_TRACEID,
> +	TRACECMD_OPTION_TIME_SHIFT,
>  };
>  
>  enum {
> @@ -130,6 +131,11 @@ void tracecmd_set_flag(struct tracecmd_input *handle, int flag);
>  void tracecmd_clear_flag(struct tracecmd_input *handle, int flag);
>  unsigned long tracecmd_get_flags(struct tracecmd_input *handle);
>  unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle);
> +int tracecmd_get_guest_cpumap(struct tracecmd_input *handle,
> +			      unsigned long long trace_id,
> +			      char *name,
> +			      int *vcpu_count, int *cpu_pid);
> +unsigned long long tracecmd_get_tsync_peer(struct tracecmd_input *handle);
>  
>  void tracecmd_parse_trace_clock(struct tracecmd_input *handle, char *file, int size);
>  
> diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
> index 40d9825..a6b675a 100644
> --- a/lib/trace-cmd/trace-input.c
> +++ b/lib/trace-cmd/trace-input.c
> @@ -74,6 +74,18 @@ struct input_buffer_instance {
>  	size_t			offset;
>  };
>  
> +struct ts_offset_sample {
> +	long long	time;
> +	long long	offset;
> +};
> +
> +struct host_trace_info {
> +	bool			sync_enable;
> +	unsigned long long	trace_id;
> +	int			ts_samples_count;
> +	struct ts_offset_sample	*ts_samples;
> +};
> +
>  struct tracecmd_input {
>  	struct tep_handle	*pevent;
>  	struct tep_plugin_list	*plugin_list;
> @@ -92,6 +104,7 @@ struct tracecmd_input {
>  	bool			use_pipe;
>  	struct cpu_data 	*cpu_data;
>  	long long		ts_offset;
> +	struct host_trace_info	host;
>  	double			ts2secs;
>  	char *			cpustats;
>  	char *			uname;
> @@ -1072,6 +1085,67 @@ static void free_next(struct tracecmd_input *handle, int cpu)
>  	free_record(record);
>  }
>  
> +static inline unsigned long long
> +timestamp_correction_calc(unsigned long long ts, struct ts_offset_sample *min,
> +			  struct ts_offset_sample *max)
> +{
> +	long long tscor = min->offset +
> +			(((((long long)ts) - min->time)*
> +			(max->offset-min->offset))/(max->time-min->time));

When every I see a division like this, I'd like to think we should add:

	long long offset = ((long long)ts - min->time) *
			(max->offset - min->offset);
	long long delta = max->time - min->time;
	long long tscor = min->offset +
			(offset + delta / 2) / delta;

This handles rounding of delta instead of truncating. Also, it's best
to have spaces around operators, as it's hard to see where the variable
stops and the operation beings with max->time-min->time.

> +
> +	if (tscor < 0)
> +		return ts - llabs(tscor);

I'm curious to why the test and using llabs, instead of just returning:

	ts + tscor ?

Is there a difference between that when tscor is negative and using
llabs?

> +
> +	return ts + tscor;
> +}
> +
> +static unsigned long long timestamp_correct(unsigned long long ts,
> +					    struct tracecmd_input *handle)
> +{
> +	struct host_trace_info	*host = &handle->host;
> +	int min, mid, max;
> +
> +	if (handle->ts_offset)
> +		return ts + handle->ts_offset;
> +
> +	if (!host->sync_enable || !host->ts_samples_count || !host->ts_samples)
> +		return ts;

Hmm, perhaps we should make host->sync_enable false when
host->ts_samples_count or host->ts_samples are NULL and remove the
extra checks? (slight optimization)

> +
> +	/* We have one sample, nothing to calc here */
> +	if (host->ts_samples_count == 1)
> +		return ts + host->ts_samples[0].offset;
> +
> +	/* We have two samples, nothing to search here */
> +	if (host->ts_samples_count == 2)
> +		return timestamp_correction_calc(ts, &host->ts_samples[0],
> +						 &host->ts_samples[1]);
> +
> +	/* We have more than two samples */
> +	if (ts <= host->ts_samples[0].time)
> +		return timestamp_correction_calc(ts,
> +						 &host->ts_samples[0],
> +						 &host->ts_samples[1]);
> +	else if (ts >= host->ts_samples[host->ts_samples_count-1].time)
> +		return timestamp_correction_calc(ts,
> +						 &host->ts_samples[host->ts_samples_count-2],
> +						 &host->ts_samples[host->ts_samples_count-1]);
> +	min = 0;
> +	max = host->ts_samples_count-1;
> +	mid = (min + max)/2;
> +	while (min <= max) {
> +		if (ts < host->ts_samples[mid].time)
> +			max = mid - 1;
> +		else if (ts > host->ts_samples[mid].time)
> +			min = mid + 1;
> +		else
> +			break;
> +		mid = (min + max)/2;
> +	}

Hmm, probably should libc's bsearch() instead of open coding a binary
search.

> +
> +	return timestamp_correction_calc(ts, &host->ts_samples[mid],
> +					 &host->ts_samples[mid+1]);
> +}
> +
>  /*
>   * Page is mapped, now read in the page header info.
>   */
> @@ -1093,7 +1167,7 @@ static int update_page_info(struct tracecmd_input *handle, int cpu)
>  		    kbuffer_subbuffer_size(kbuf));
>  		return -1;
>  	}
> -	handle->cpu_data[cpu].timestamp = kbuffer_timestamp(kbuf) + handle->ts_offset;
> +	handle->cpu_data[cpu].timestamp = timestamp_correct(kbuffer_timestamp(kbuf), handle);
>  
>  	if (handle->ts2secs)
>  		handle->cpu_data[cpu].timestamp *= handle->ts2secs;
> @@ -1820,7 +1894,7 @@ read_again:
>  		goto read_again;
>  	}
>  
> -	handle->cpu_data[cpu].timestamp = ts + handle->ts_offset;
> +	handle->cpu_data[cpu].timestamp = timestamp_correct(ts, handle);
>  
>  	if (handle->ts2secs) {
>  		handle->cpu_data[cpu].timestamp *= handle->ts2secs;
> @@ -2143,6 +2217,47 @@ void tracecmd_set_ts2secs(struct tracecmd_input *handle,
>  	handle->use_trace_clock = false;
>  }
>  
> +static int tsync_offset_cmp(const void *a, const void *b)
> +{
> +	struct ts_offset_sample *ts_a = (struct ts_offset_sample *)a;
> +	struct ts_offset_sample *ts_b = (struct ts_offset_sample *)b;
> +
> +	if (ts_a->time > ts_b->time)
> +		return 1;
> +	if (ts_a->time < ts_b->time)
> +		return -1;
> +	return 0;
> +}

We have the compare function for bsearch() here.

> +
> +static void tsync_offset_load(struct tracecmd_input *handle, char *buf)
> +{
> +	struct host_trace_info *host = &handle->host;
> +	long long *buf8 = (long long *)buf;
> +	int i, j;
> +
> +	for (i = 0; i < host->ts_samples_count; i++) {
> +		host->ts_samples[i].time = tep_read_number(handle->pevent,
> +							   buf8 + i, 8);
> +		host->ts_samples[i].offset = tep_read_number(handle->pevent,
> +						buf8 + host->ts_samples_count+i, 8);
> +	}
> +	qsort(host->ts_samples, host->ts_samples_count,
> +	      sizeof(struct ts_offset_sample), tsync_offset_cmp);
> +	/* Filter possible samples with equal time */
> +	for (i = 0, j = 0; i < host->ts_samples_count; i++) {
> +		if (i == 0 || host->ts_samples[i].time != host->ts_samples[i-1].time)
> +			host->ts_samples[j++] = host->ts_samples[i];
> +	}
> +	host->ts_samples_count = j;
> +	host->sync_enable = true;
> +}
> +
> +static void trace_tsync_offset_free(struct host_trace_info *host)
> +{
> +	free(host->ts_samples);
> +	host->ts_samples = NULL;
> +}
> +
>  static int trace_pid_map_cmp(const void *a, const void *b)
>  {
>  	struct tracecmd_proc_addr_map *m_a = (struct tracecmd_proc_addr_map *)a;
> @@ -2323,6 +2438,7 @@ static int handle_options(struct tracecmd_input *handle)
>  	struct input_buffer_instance *buffer;
>  	struct hook_list *hook;
>  	char *buf;
> +	int sampes_size;

"sampes"? Is this short for "samples"? Just use "samples_size" then.


>  	int cpus;
>  
>  	/* By default, use usecs, unless told otherwise */
> @@ -2370,6 +2486,28 @@ static int handle_options(struct tracecmd_input *handle)
>  			offset = strtoll(buf, NULL, 0);
>  			handle->ts_offset += offset;
>  			break;
> +		case TRACECMD_OPTION_TIME_SHIFT:
> +			/*
> +			 * long long int (8 bytes) trace session ID
> +			 * int (4 bytes) count of timestamp offsets.
> +			 * long long array of size [count] of times,
> +			 *      when the offsets were calculated.
> +			 * long long array of size [count] of timestamp offsets.
> +			 */
> +			if (size < 12 || handle->flags & TRACECMD_FL_IGNORE_DATE)
> +				break;
> +			handle->host.trace_id = tep_read_number(handle->pevent,
> +								buf, 8);
> +			handle->host.ts_samples_count = tep_read_number(handle->pevent,
> +									buf + 8, 4);
> +			sampes_size = (8 * handle->host.ts_samples_count);
> +			if (size != (12 + (2 * sampes_size)))
> +				break;
> +			handle->host.ts_samples = malloc(2 * sampes_size);
> +			if (!handle->host.ts_samples)
> +				return -ENOMEM;
> +			tsync_offset_load(handle, buf + 12);
> +			break;
>  		case TRACECMD_OPTION_CPUSTAT:
>  			buf[size-1] = '\n';
>  			cpustats = realloc(cpustats, cpustats_size + size + 1);
> @@ -3078,6 +3216,8 @@ void tracecmd_close(struct tracecmd_input *handle)
>  	trace_pid_map_free(handle->pid_maps);
>  	handle->pid_maps = NULL;
>  
> +	trace_tsync_offset_free(&handle->host);
> +
>  	if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
>  		tracecmd_close(handle->parent);
>  	else {
> @@ -3532,3 +3672,29 @@ unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
>  {
>  	return handle->trace_id;
>  }
> +
> +/**
> + * tracecmd_get_tsync_peer - get the trace session id of the peer host
> + * @handle: input handle for the trace.dat file
> + *
> + * Returns the trace id of the peer host, written in the trace file
> + *
> + * This information is stored in guest trace.dat file
> + */
> +unsigned long long tracecmd_get_tsync_peer(struct tracecmd_input *handle)
> +{
> +	return handle->host.trace_id;
> +}
> +
> +/**
> + * tracecmd_enable_tsync - enable / disable the timestamps correction
> + * @handle: input handle for the trace.dat file
> + * @enable: enable / disable the timestamps correction
> + *
> + * Enables or disables timestamps correction on file load, using the array of
> + * recorded time offsets
> + */
> +void tracecmd_enable_tsync(struct tracecmd_input *handle, bool enable)
> +{

Perhaps here we should check if samples are allocated already, and only
allow it to be enabled if they are.

-- Steve


> +	handle->host.sync_enable = enable;
> +}


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 13/18] trace-cmd: Add guest information in host's trace.dat file
  2019-12-03 10:35 ` [PATCH v17 13/18] trace-cmd: Add guest information in host's trace.dat file Tzvetomir Stoyanov (VMware)
@ 2019-12-05  0:59   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-05  0:59 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:17 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> New trace.dat option is introduced: TRACECMD_OPTION_GUEST.
> Written in the host's trace.dat file, it contains information about
> guests, traced at the same time: guest trace ID, number of VCPUs and
> PIDs of the host tasks, running those VCPU. The data is stored in
> the file as NULL terminated string:
> 	"Guest %s %llu %d\n" -> guest name, number of VCPUs
> 	"%d %d\n" -> VCPU, PID of host task
> 	.....
> 	"%d %d\n" -> VCPU, PID of host task
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>  include/trace-cmd/trace-cmd.h |   1 +
>  lib/trace-cmd/trace-input.c   | 113 ++++++++++++++++++++++++++++++++++
>  tracecmd/trace-record.c       |  45 ++++++++++++++
>  3 files changed, 159 insertions(+)
> 
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index 1240b4a..17badf2 100644
> --- a/include/trace-cmd/trace-cmd.h
> +++ b/include/trace-cmd/trace-cmd.h
> @@ -88,6 +88,7 @@ enum {
>  	TRACECMD_OPTION_PROCMAPS,
>  	TRACECMD_OPTION_TRACEID,
>  	TRACECMD_OPTION_TIME_SHIFT,
> +	TRACECMD_OPTION_GUEST,
>  };
>  
>  enum {
> diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
> index a6b675a..e36ebaa 100644
> --- a/lib/trace-cmd/trace-input.c
> +++ b/lib/trace-cmd/trace-input.c
> @@ -79,6 +79,14 @@ struct ts_offset_sample {
>  	long long	offset;
>  };
>  
> +struct guest_trace_info {
> +	struct guest_trace_info	*next;
> +	char			*name;
> +	unsigned long long	trace_id;
> +	int			vcpu_count;
> +	int			*cpu_pid;
> +};
> +
>  struct host_trace_info {
>  	bool			sync_enable;
>  	unsigned long long	trace_id;
> @@ -112,6 +120,7 @@ struct tracecmd_input {
>  	char *			trace_clock;
>  	struct input_buffer_instance	*buffers;
>  	int			parsing_failures;
> +	struct guest_trace_info	*guest;
>  
>  	struct tracecmd_ftrace	finfo;
>  
> @@ -2294,6 +2303,73 @@ static int trace_traceid_load(struct tracecmd_input *handle, char *buf)
>  	return -1;
>  }
>  
> +static void trace_guests_free(struct tracecmd_input *handle)
> +{
> +	struct guest_trace_info *guest;
> +
> +	while (handle->guest) {
> +		guest = handle->guest;
> +		handle->guest = handle->guest->next;
> +		free(guest->name);
> +		free(guest->cpu_pid);
> +		free(guest);
> +	}
> +}
> +
> +static int trace_guest_load(struct tracecmd_input *handle, char *buf)
> +{
> +	struct guest_trace_info *guest = NULL;
> +	unsigned long long tid;
> +	char *line;
> +	int cpu, pid;
> +
> +	line = strchr(buf, '\n');
> +	if (!line)
> +		goto error;
> +	*line = '\0';
> +
> +	guest = calloc(1, sizeof(struct guest_trace_info));
> +	if (!guest)
> +		goto error;
> +
> +	if (sscanf(buf, "%*s %ms %llu %d", &guest->name, &tid, &cpu) != 3)
> +		goto error;
> +	guest->trace_id = tid;
> +	guest->vcpu_count = cpu;
> +
> +	guest->cpu_pid = calloc(guest->vcpu_count, sizeof(int));
> +	if (!guest->cpu_pid)
> +		goto error;
> +
> +	buf = line + 1;
> +	line = strchr(buf, '\n');
> +	while (line) {
> +		*line = '\0';
> +		if (sscanf(buf, "%d %d", &cpu, &pid) != 2)
> +			goto error;
> +
> +		if (cpu < guest->vcpu_count)
> +			guest->cpu_pid[cpu] = pid;
> +		buf = line + 1;
> +		if (!*buf)
> +			break;
> +
> +		line = strchr(buf, '\n');

Hmm, strtok_r() seems more appropriate here than strchr(), then you
don't need to deal with the *line = '\0'.


> +	}
> +
> +	guest->next = handle->guest;
> +	handle->guest = guest;
> +	return 0;
> +
> +error:
> +	if (guest) {
> +		free(guest->cpu_pid);
> +		free(guest->name);
> +		free(guest);
> +	}
> +	return -1;
> +}
> +
>  #define STR_PROCMAP_LINE_MAX	(PATH_MAX+22)
>  static int trace_pid_map_load(struct tracecmd_input *handle, char *buf)
>  {
> @@ -2558,6 +2634,10 @@ static int handle_options(struct tracecmd_input *handle)
>  			if (buf[size-1] == '\0')
>  				trace_traceid_load(handle, buf);
>  			break;
> +		case TRACECMD_OPTION_GUEST:
> +			if (buf[size-1] == '\0')
> +				trace_guest_load(handle, buf);
> +			break;
>  		default:
>  			warning("unknown option %d", option);
>  			break;
> @@ -3217,6 +3297,7 @@ void tracecmd_close(struct tracecmd_input *handle)
>  	handle->pid_maps = NULL;
>  
>  	trace_tsync_offset_free(&handle->host);
> +	trace_guests_free(handle);
>  
>  	if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
>  		tracecmd_close(handle->parent);
> @@ -3673,6 +3754,38 @@ unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
>  	return handle->trace_id;
>  }
>  
> +/**
> + * tracecmd_get_guest_cpumap - get the mapping of guest VCPU to host process
> + * @handle: input handle for the trace.dat file
> + * @trace_id: ID of the guest tracing session

@name, @vcpu_count, and @cpu_pid should still be up in the above
section.

> + *
> + * Returns @name of the guest, number of VPUs (@vcpu_count)
> + * and array @cpu_pid with size @vcpu_count. Array index is VCPU id, array
> + * content is PID of the host process, running this VCPU.
> + *
> + * This information is stored in host trace.dat file
> + */
> +int tracecmd_get_guest_cpumap(struct tracecmd_input *handle,
> +			      unsigned long long trace_id,
> +			      char *name,

name isn't returned.

> +			      int *vcpu_count, int *cpu_pid)
> +{
> +	struct guest_trace_info	*guest = handle->guest;
> +
> +	while (guest) {
> +		if (guest->trace_id == trace_id)
> +			break;
> +		guest = guest->next;
> +	}
> +	if (!guest)
> +		return -1;
> +
> +	name = guest->name;

The above doesn't do anything, and will probably be optimized out by
the compiler.

-- Steve


> +	*vcpu_count = guest->vcpu_count;
> +	cpu_pid = guest->cpu_pid;
> +	return 0;
> +}
> +
>  /**
>   * tracecmd_get_tsync_peer - get the trace session id of the peer host
>   * @handle: input handle for the trace.dat file
> diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
> index e51134f..49730d6 100644
> --- a/tracecmd/trace-record.c
> +++ b/tracecmd/trace-record.c
> @@ -2884,6 +2884,19 @@ struct guest {
>  static struct guest *guests;
>  static size_t guests_len;
>  
> +static struct guest *get_guest_info(unsigned int guest_cid)
> +{
> +	int i;
> +
> +	if (!guests)
> +		return NULL;
> +
> +	for (i = 0; i < guests_len; i++)
> +		if (guest_cid == guests[i].cid)
> +			return guests + i;
> +	return NULL;
> +}
> +
>  static char *get_qemu_guest_name(char *arg)
>  {
>  	char *tok, *end = arg;
> @@ -3689,6 +3702,33 @@ static void append_buffer(struct tracecmd_output *handle,
>  	}
>  }
>  
> +static void
> +add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance)
> +{
> +	struct guest *guest = get_guest_info(instance->cid);
> +	struct trace_seq s;
> +	int i;
> +
> +	if (!guest)
> +		return;
> +	for (i = 0; i < VCPUS_MAX; i++)
> +		if (!guest->cpu_pid[i])
> +			break;
> +
> +	trace_seq_init(&s);
> +
> +	trace_seq_printf(&s, "Guest %s %llu %d\n",
> +			 guest->name, instance->trace_id, i);
> +	for (i = 0; i < VCPUS_MAX; i++) {
> +		if (!guest->cpu_pid[i])
> +			break;
> +		trace_seq_printf(&s, "%d %d\n", i, guest->cpu_pid[i]);
> +	}
> +	trace_seq_terminate(&s);
> +	tracecmd_add_option(handle, TRACECMD_OPTION_GUEST,
> +			    s.len + 1, s.buffer);
> +	trace_seq_destroy(&s);
> +}
>  
>  static void
>  add_pid_maps(struct tracecmd_output *handle, struct buffer_instance *instance)
> @@ -3976,6 +4016,11 @@ static void record_data(struct common_record_context *ctx)
>  			add_pid_maps(handle, instance);
>  		}
>  
> +		for_all_instances(instance) {
> +			if (is_guest(instance))
> +				add_guest_info(handle, instance);
> +		}
> +
>  		tracecmd_append_cpu_data(handle, local_cpu_count, temp_files);
>  
>  		for (i = 0; i < max_cpu_count; i++)


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances.
  2019-12-04 16:17   ` Steven Rostedt
@ 2019-12-05 14:40     ` Tzvetomir Stoyanov
  0 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2019-12-05 14:40 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

On Wed, Dec 4, 2019 at 6:17 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
[ ... ]
> > +     do {
> > +             r = read(fd, buffer, BUFSIZ);
> > +             if (r <= 0)
> > +                     continue;
> > +             if (size)
> > +                     buf = realloc(buf, size+r+1);
> > +             else
> > +                     buf = malloc(r+1);
>
> I know this is only cut and pasted from what the original was, but we
> should probably (either in this patch, or perhaps a separate one) fix
> the above. As realloc(NULL, x) is equivalent to malloc(x), we can just
> have:
>
>                 buf = realloc(buf, size+r+1);
>
>
> > +             if (!buf)
> > +                     die("Failed to allocate instance file buffer");
>
> As this is becoming a library function, we should remove "die()", as
> that is only allowed in the application. Not library calls.
>
> That would also mean we need to clean up anything we allocated on
> failure, and that the realloc would need to be:
>
>                 new_buf = realloc(buf, size + r + 1);
>
>                 if (!new_buf) {
>                         free(buf);
>                         return NULL;
>
> All the functions that are being moved into the library needs to have
> their die() calls removed. This is OK to do in a separate patch.
>
> Preferably right after this patch (so reviewers know it's happening).
>
I fixed most of the comments, except those die() call. There are a lot
of things that
should be fixed in the existing libtracecmd code, in order to become a
real library.
Also, I started to work on a new library providing APIs for
interaction with files
from tracefs, libtraceftrace (this is only a work name, we should
think about more suitable one).
I would suggest changes from this patch set to be considered only as
part of the time stamp sync
feature. There will be at least two library patch sets - for cleaning
up the existing libtracecmd code and
for the new ftrace library.

>
> > +             memcpy(buf+size, buffer, r);
> > +             size += r;
> > +     } while (r);
> > +
> > +     buf[size] = '\0';
> > +     if (psize)
> > +             *psize = size;
> > +     return buf;
> > +}
> > +
> > +/**
> > + * tracecmd_set_clock - Set the clock of ftrace event's timestamps, per instance.
> > + * @instance: Pointer to ftrace instance, containing the desired clock.
> > + * @old_clock: Optional, return the newly allocated string with the old clock.
>
> This looks to be just a copy of the old code and forced into a library
> function. As a library function, it should return an "int" and be
> passed the clock name directly. Not via the instance->clock.

The idea of this API is to apply the clock, already set to the
instance. This follows
the existing logic of configuring the instance clock, that's why I put
the "char *clock"
in the new "struct tracecmd_instance". We can remove the clock from
the structure
and pass it as an argument to tracecmd_set_clock(), so the  "struct
tracecmd_instance"
will hold only the instance name ?

>
> -- Steve
>
> > + *
> > + */
> > +void tracecmd_set_clock(struct tracecmd_instance *instance, char **old_clock)
> > +{
> > +     char *content;
> > +     char *str;
> > +
> > +     if (!instance->clock)
> > +             return;
> > +
> > +     /* The current clock is in brackets, reset it when we are done */
> > +     content = tracecmd_read_instance_file(instance, "trace_clock", NULL);
> > +
> > +     /* check if first clock is set */
> > +     if (*content == '[')
> > +             str = strtok(content+1, "]");
> > +     else {
> > +             str = strtok(content, "[");
> > +             if (!str)
> > +                     die("Can not find clock in trace_clock");
> > +             str = strtok(NULL, "]");
> > +     }
> > +     if (old_clock)
> > +             *old_clock = strdup(str);
> > +
> > +     free(content);
> > +     tracecmd_write_instance_file(instance,
> > +                                  "trace_clock", instance->clock, "clock");
> > +}
> >

Thanks !

-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT
  2019-12-05  0:46   ` Steven Rostedt
@ 2019-12-05 15:09     ` Tzvetomir Stoyanov
  0 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2019-12-05 15:09 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

On Thu, Dec 5, 2019 at 2:46 AM Steven Rostedt <rostedt@goodmis.org> wrote:
[ ... ]
> > +static inline unsigned long long
> > +timestamp_correction_calc(unsigned long long ts, struct ts_offset_sample *min,
> > +                       struct ts_offset_sample *max)
> > +{
> > +     long long tscor = min->offset +
> > +                     (((((long long)ts) - min->time)*
> > +                     (max->offset-min->offset))/(max->time-min->time));
>
> When every I see a division like this, I'd like to think we should add:
>
>         long long offset = ((long long)ts - min->time) *
>                         (max->offset - min->offset);
>         long long delta = max->time - min->time;
>         long long tscor = min->offset +
>                         (offset + delta / 2) / delta;
>
> This handles rounding of delta instead of truncating. Also, it's best
> to have spaces around operators, as it's hard to see where the variable
> stops and the operation beings with max->time-min->time.
>
> > +
> > +     if (tscor < 0)
> > +             return ts - llabs(tscor);
>
> I'm curious to why the test and using llabs, instead of just returning:
>
>         ts + tscor ?
>
> Is there a difference between that when tscor is negative and using
> llabs?
>
The only reason to use llabs() is mixed signed - unsigned calculation. As time
correction tscor can be negative,  will it be converted to unsigned in
"ts + tscor" ?

> > +
> > +     return ts + tscor;
> > +}
> > +
> > +static unsigned long long timestamp_correct(unsigned long long ts,
> > +                                         struct tracecmd_input *handle)
> > +{
> > +     struct host_trace_info  *host = &handle->host;
> > +     int min, mid, max;
> > +
> > +     if (handle->ts_offset)
> > +             return ts + handle->ts_offset;
> > +
> > +     if (!host->sync_enable || !host->ts_samples_count || !host->ts_samples)
> > +             return ts;
>
> Hmm, perhaps we should make host->sync_enable false when
> host->ts_samples_count or host->ts_samples are NULL and remove the
> extra checks? (slight optimization)
>
> > +
> > +     /* We have one sample, nothing to calc here */
> > +     if (host->ts_samples_count == 1)
> > +             return ts + host->ts_samples[0].offset;
> > +
> > +     /* We have two samples, nothing to search here */
> > +     if (host->ts_samples_count == 2)
> > +             return timestamp_correction_calc(ts, &host->ts_samples[0],
> > +                                              &host->ts_samples[1]);
> > +
> > +     /* We have more than two samples */
> > +     if (ts <= host->ts_samples[0].time)
> > +             return timestamp_correction_calc(ts,
> > +                                              &host->ts_samples[0],
> > +                                              &host->ts_samples[1]);
> > +     else if (ts >= host->ts_samples[host->ts_samples_count-1].time)
> > +             return timestamp_correction_calc(ts,
> > +                                              &host->ts_samples[host->ts_samples_count-2],
> > +                                              &host->ts_samples[host->ts_samples_count-1]);
> > +     min = 0;
> > +     max = host->ts_samples_count-1;
> > +     mid = (min + max)/2;
> > +     while (min <= max) {
> > +             if (ts < host->ts_samples[mid].time)
> > +                     max = mid - 1;
> > +             else if (ts > host->ts_samples[mid].time)
> > +                     min = mid + 1;
> > +             else
> > +                     break;
> > +             mid = (min + max)/2;
> > +     }
>
> Hmm, probably should libc's bsearch() instead of open coding a binary
> search.
>

I do not use bsearch() as there is no exact match it that search - only the
interval in which the timestamp fits should be found. May be there is a way
to use bsearch() in this scenario, I can think about it.

[ ...]

> >  }
> >
> > +static int tsync_offset_cmp(const void *a, const void *b)
> > +{
> > +     struct ts_offset_sample *ts_a = (struct ts_offset_sample *)a;
> > +     struct ts_offset_sample *ts_b = (struct ts_offset_sample *)b;
> > +
> > +     if (ts_a->time > ts_b->time)
> > +             return 1;
> > +     if (ts_a->time < ts_b->time)
> > +             return -1;
> > +     return 0;
> > +}
>
> We have the compare function for bsearch() here.
>

We can use it only for exact match, cannot use the same function for finding the
interval.

[ ... ]

> >  static int trace_pid_map_cmp(const void *a, const void *b)
> >  {
> >       struct tracecmd_proc_addr_map *m_a = (struct tracecmd_proc_addr_map *)a;
> > @@ -2323,6 +2438,7 @@ static int handle_options(struct tracecmd_input *handle)
> >       struct input_buffer_instance *buffer;
> >       struct hook_list *hook;
> >       char *buf;
> > +     int sampes_size;
>
> "sampes"? Is this short for "samples"? Just use "samples_size" then.
>

It's a typo, fixed it :)

>
> >       int cpus;
> >
> >       /* By default, use usecs, unless told otherwise */
> > @@ -2370,6 +2486,28 @@ static int handle_options(struct tracecmd_input *handle)
> >                       offset = strtoll(buf, NULL, 0);
> >                       handle->ts_offset += offset;
> >                       break;
> > +             case TRACECMD_OPTION_TIME_SHIFT:
> > +                     /*
> > +                      * long long int (8 bytes) trace session ID
> > +                      * int (4 bytes) count of timestamp offsets.
> > +                      * long long array of size [count] of times,
> > +                      *      when the offsets were calculated.
> > +                      * long long array of size [count] of timestamp offsets.
> > +                      */
> > +                     if (size < 12 || handle->flags & TRACECMD_FL_IGNORE_DATE)
> > +                             break;
> > +                     handle->host.trace_id = tep_read_number(handle->pevent,
> > +                                                             buf, 8);
> > +                     handle->host.ts_samples_count = tep_read_number(handle->pevent,
> > +                                                                     buf + 8, 4);
> > +                     sampes_size = (8 * handle->host.ts_samples_count);
> > +                     if (size != (12 + (2 * sampes_size)))
> > +                             break;
> > +                     handle->host.ts_samples = malloc(2 * sampes_size);
> > +                     if (!handle->host.ts_samples)
> > +                             return -ENOMEM;
> > +                     tsync_offset_load(handle, buf + 12);
> > +                     break;
> >               case TRACECMD_OPTION_CPUSTAT:
> >                       buf[size-1] = '\n';
> >                       cpustats = realloc(cpustats, cpustats_size + size + 1);
> > @@ -3078,6 +3216,8 @@ void tracecmd_close(struct tracecmd_input *handle)
> >       trace_pid_map_free(handle->pid_maps);
> >       handle->pid_maps = NULL;
> >
> > +     trace_tsync_offset_free(&handle->host);
> > +
> >       if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
> >               tracecmd_close(handle->parent);
> >       else {
> > @@ -3532,3 +3672,29 @@ unsigned long long tracecmd_get_traceid(struct tracecmd_input *handle)
> >  {
> >       return handle->trace_id;
> >  }
> > +
> > +/**
> > + * tracecmd_get_tsync_peer - get the trace session id of the peer host
> > + * @handle: input handle for the trace.dat file
> > + *
> > + * Returns the trace id of the peer host, written in the trace file
> > + *
> > + * This information is stored in guest trace.dat file
> > + */
> > +unsigned long long tracecmd_get_tsync_peer(struct tracecmd_input *handle)
> > +{
> > +     return handle->host.trace_id;
> > +}
> > +
> > +/**
> > + * tracecmd_enable_tsync - enable / disable the timestamps correction
> > + * @handle: input handle for the trace.dat file
> > + * @enable: enable / disable the timestamps correction
> > + *
> > + * Enables or disables timestamps correction on file load, using the array of
> > + * recorded time offsets
> > + */
> > +void tracecmd_enable_tsync(struct tracecmd_input *handle, bool enable)
> > +{
>
> Perhaps here we should check if samples are allocated already, and only
> allow it to be enabled if they are.
>
> -- Steve
>
>
> > +     handle->host.sync_enable = enable;
> > +}
>

Thanks !

-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument
  2019-12-03 10:35 ` [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument Tzvetomir Stoyanov (VMware)
@ 2019-12-09 19:31   ` Steven Rostedt
  2019-12-10  8:49     ` Tzvetomir Stoyanov
  0 siblings, 1 reply; 42+ messages in thread
From: Steven Rostedt @ 2019-12-09 19:31 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:18 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> When tracing host and guest machines, both should use the same
> tracing clock for event timestamps. If a clock is specified
> as host tracing argument, with option "-C clock_name", the same
> is injected as guest tracing argument. If the user wants to use
> different tracing clocks, it can specify it using "-C clock_name"
> as guest tracing argument. In that case, the one specified by
> the user has higher priority.
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>  tracecmd/trace-record.c | 19 +++++++++++++++++--
>  1 file changed, 17 insertions(+), 2 deletions(-)
> 
> diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
> index 49730d6..e7fb1bd 100644
> --- a/tracecmd/trace-record.c
> +++ b/tracecmd/trace-record.c
> @@ -5405,6 +5405,8 @@ static void parse_record_options(int argc,
>  	char *sav;
>  	int name_counter = 0;
>  	int neg_event = 0;
> +	struct buffer_instance *instance;
> +	bool guest_config = false;

Should have a different name, like "clock_set", as "guest_config"
doesn't let us know what this is about.


>  
>  	init_common_record_context(ctx, curr_cmd);
>  
> @@ -5562,6 +5564,7 @@ static void parse_record_options(int argc,
>  			break;
>  		case 'C':
>  			ctx->instance->ftrace->clock = optarg;
> +			guest_config = true;
>  			break;
>  		case 'v':
>  			neg_event = 1;
> @@ -5779,14 +5782,26 @@ static void parse_record_options(int argc,
>  
>  	/* If --date is specified, prepend it to all guest VM flags */
>  	if (ctx->date) {
> -		struct buffer_instance *instance;
> -
>  		for_all_instances(instance) {
>  			if (is_guest(instance))
>  				add_argv(instance, "--date", true);
>  		}
>  	}
>  
> +	if (guest_config) {
> +		/* If -C is specified, prepend clock to all guest VM flags */
> +		for_all_instances(instance) {
> +			if (top_instance.ftrace->clock) {
> +				if (is_guest(instance)) {

We should only append this, if the guest didn't have a clock set
already. As the change log seems to say, if the user states a "-C
clock" for the guest, that should take precedence over the host clock
set. That is, a user may specifically state that they are using a
different clock. If we have frequency and offset set, it should still
work with different clocks.

-- Steve


> +					add_argv(instance,
> +						 (char *)top_instance.ftrace->clock,
> +						 true);
> +					add_argv(instance, "-C", true);
> +				}
> +			}
> +		}
> +	}
> +
>  	if (!ctx->filtered && ctx->instance->filter_mod)
>  		add_func(&ctx->instance->filter_funcs,
>  			 ctx->instance->filter_mod, "*");


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 15/18] trace-cmd: Refactor few trace-cmd internal functions.
  2019-12-03 10:35 ` [PATCH v17 15/18] trace-cmd: Refactor few trace-cmd internal functions Tzvetomir Stoyanov (VMware)
@ 2019-12-09 19:32   ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-09 19:32 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:19 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> In order to reuse code inside trace-cmd application context,
> few functions are made non static:
> 	int make_vsock(unsigned int port);
> 	int get_vsock_port(int sd, unsigned int *port);
> 	int open_vsock(unsigned int cid, unsigned int port);
> 	char *get_guest_file(const char *file, const char *guest);

We should probably rename them to have a "trace_" prefix. This way we
know what functions are not static, but "trace_" means it's local,
where as "tracecmd_" means its exported.

-- Steve

> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument
  2019-12-09 19:31   ` Steven Rostedt
@ 2019-12-10  8:49     ` Tzvetomir Stoyanov
  2019-12-10 15:48       ` Steven Rostedt
  0 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2019-12-10  8:49 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

On Mon, Dec 9, 2019 at 9:31 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
[ ... ]
> > diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
> > index 49730d6..e7fb1bd 100644
> > --- a/tracecmd/trace-record.c
> > +++ b/tracecmd/trace-record.c
> > @@ -5405,6 +5405,8 @@ static void parse_record_options(int argc,
> >       char *sav;
> >       int name_counter = 0;
> >       int neg_event = 0;
> > +     struct buffer_instance *instance;
> > +     bool guest_config = false;
>
> Should have a different name, like "clock_set", as "guest_config"
> doesn't let us know what this is about.

I use the same flag in patch 16 from the same series, with the same purpose -
to apply host config to the guest. That's why decided to call it "guest_config".

>
>
> >
> >       init_common_record_context(ctx, curr_cmd);
> >
> > @@ -5562,6 +5564,7 @@ static void parse_record_options(int argc,
> >                       break;
> >               case 'C':
> >                       ctx->instance->ftrace->clock = optarg;
> > +                     guest_config = true;
> >                       break;
> >               case 'v':
> >                       neg_event = 1;
> > @@ -5779,14 +5782,26 @@ static void parse_record_options(int argc,
> >
> >       /* If --date is specified, prepend it to all guest VM flags */
> >       if (ctx->date) {
> > -             struct buffer_instance *instance;
> > -
> >               for_all_instances(instance) {
> >                       if (is_guest(instance))
> >                               add_argv(instance, "--date", true);
> >               }
> >       }
> >
> > +     if (guest_config) {
> > +             /* If -C is specified, prepend clock to all guest VM flags */
> > +             for_all_instances(instance) {
> > +                     if (top_instance.ftrace->clock) {
> > +                             if (is_guest(instance)) {
>
> We should only append this, if the guest didn't have a clock set
> already. As the change log seems to say, if the user states a "-C
> clock" for the guest, that should take precedence over the host clock
> set. That is, a user may specifically state that they are using a
> different clock. If we have frequency and offset set, it should still
> work with different clocks.

The guest config string is not parsed in the host context, that's why the host
doesn't know if a guest has an explicit  "-C clock" argument.
I can parse the guest config here, but this will complicate the implementation.
Using the current approach still guarantees that the user specified
config has higher
priority than injected one - add_argv() API prepends to the beginning
of the string, so
user arguments are always after the injected one. When guest parses
the string, in case of
duplicated "-C clock" arguments, the last one wins.


>
> -- Steve
>
>
> > +                                     add_argv(instance,
> > +                                              (char *)top_instance.ftrace->clock,
> > +                                              true);
> > +                                     add_argv(instance, "-C", true);
> > +                             }
> > +                     }
> > +             }
> > +     }
> > +
> >       if (!ctx->filtered && ctx->instance->filter_mod)
> >               add_func(&ctx->instance->filter_funcs,
> >                        ctx->instance->filter_mod, "*");
>

Thanks !

-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument
  2019-12-10  8:49     ` Tzvetomir Stoyanov
@ 2019-12-10 15:48       ` Steven Rostedt
  2019-12-11  8:21         ` Tzvetomir Stoyanov
  0 siblings, 1 reply; 42+ messages in thread
From: Steven Rostedt @ 2019-12-10 15:48 UTC (permalink / raw)
  To: Tzvetomir Stoyanov; +Cc: linux-trace-devel

On Tue, 10 Dec 2019 10:49:43 +0200
Tzvetomir Stoyanov <tz.stoyanov@gmail.com> wrote:

> > > +     if (guest_config) {
> > > +             /* If -C is specified, prepend clock to all guest VM flags */
> > > +             for_all_instances(instance) {
> > > +                     if (top_instance.ftrace->clock) {
> > > +                             if (is_guest(instance)) {  
> >
> > We should only append this, if the guest didn't have a clock set
> > already. As the change log seems to say, if the user states a "-C
> > clock" for the guest, that should take precedence over the host clock
> > set. That is, a user may specifically state that they are using a
> > different clock. If we have frequency and offset set, it should still
> > work with different clocks.  
> 
> The guest config string is not parsed in the host context, that's why the host
> doesn't know if a guest has an explicit  "-C clock" argument.
> I can parse the guest config here, but this will complicate the implementation.
> Using the current approach still guarantees that the user specified
> config has higher
> priority than injected one - add_argv() API prepends to the beginning
> of the string, so
> user arguments are always after the injected one. When guest parses
> the string, in case of
> duplicated "-C clock" arguments, the last one wins.

I'm confused. I'm looking at this:


	for (;;) {
		[..]
		switch (c) {
		[..]
		case 'A':
			[..]
			ctx->instance->flags |= BUFFER_FL_GUEST;
		[..]
		case 'C':
			ctx->instance->ftrace->clock = optarg;
			guest_config = true;
			break;
		[..]
		}
		[..]
	}
	[..]
	if (guest_config) {
		/* If -C is specified, prepend clock to all guest VM flags */
		for_all_instances(instance) {
			if (top_instance.ftrace->clock) {

Why can't we have here:

 if (top_instance.ftrace->clock && !instance->ftrace->clock)

If the guest instance was given a -C, I would think we don't want to add
another -C to pass to that guest?

-- Steve

				if (is_guest(instance)) {
					add_argv(instance,
						 (char *)top_instance.ftrace->clock,
						 true);
					add_argv(instance, "-C", true);
				}
			}
		}
	}

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization
  2019-12-03 10:35 ` [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization Tzvetomir Stoyanov (VMware)
@ 2019-12-10 17:04   ` Steven Rostedt
  2019-12-10 18:39   ` Steven Rostedt
  1 sibling, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-10 17:04 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

[-- Attachment #1: Type: text/plain, Size: 653 bytes --]

On Tue,  3 Dec 2019 12:35:20 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> +struct tracecmd_time_sync {
> +	unsigned int			sync_proto;
> +	int				loop_interval;
> +	sem_t				sem;

Ug, semaphores are a horrible construct. I believe you can do the same
thing here with pthread_cond variables. I attached a small program that
uses pthread_cond and pthread_mutex to do interval times like this.

Note, another thing is, you can just have a global "end" variable that
can be set and checked (as I did in my program here).

-- Steve


> +	char				*clock_str;
> +	struct tracecmd_msg_handle	*msg_handle;
> +	void				*context;
> +};
> +

[-- Attachment #2: pthread_condwait.c --]
[-- Type: text/x-c++src, Size: 2171 bytes --]

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <stdbool.h>
#include <pthread.h>

#define gettid() syscall(__NR_gettid)

static bool end;

static void __vdie(const char *fmt, va_list ap, int err)
{
	int ret = errno;

	if (err && errno)
		perror("bmp-read");
	else
		ret = -1;

	fprintf(stderr, "  ");
	vfprintf(stderr, fmt, ap);

	fprintf(stderr, "\n");
	exit(ret);
}

void pdie(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	__vdie(fmt, ap, 1);
	va_end(ap);
}

#define NR_THREADS 8
static int nr_threads = NR_THREADS;
static pthread_t *threads;

static pthread_barrier_t threads_started;

struct t_info {
	int id;
	int interval;
	pthread_mutex_t lock;
	pthread_cond_t cond;
};

static void *thread_func(void *arg)
{
	struct t_info *t = arg;
	pthread_mutex_t *this_mutex = &t->lock;
	pthread_cond_t *this_cond = &t->cond;
	struct timespec ts;
	int cnt = 0;

	pthread_barrier_wait(&threads_started);

	while (true) {

		pthread_mutex_lock(this_mutex);

		printf("task %d running %d\n", t->id, ++cnt);

		if (end)
			break;

		clock_gettime(CLOCK_REALTIME, &ts);
		ts.tv_sec += t->id + 1;
		pthread_cond_timedwait(this_cond, this_mutex, &ts);
		pthread_mutex_unlock(this_mutex);
	}

	pthread_mutex_unlock(this_mutex);

	printf("Task %d finished\n", t->id);

	return NULL;
}

int main (int argc, char **argv)
{
	struct t_info *infos;
	int ret;
	int i;

	infos = calloc(nr_threads, sizeof(*infos));
	if (!infos)
		pdie("calloc");

	threads = calloc(nr_threads, sizeof(*threads));
	if (!threads)
		pdie("calloc");

	pthread_barrier_init(&threads_started, NULL, nr_threads + 1);

	for (i = 0; i < nr_threads; i++) {
		infos[i].id = i;
		if (pthread_mutex_init(&infos[i].lock, NULL))
			pdie("pthread_mutex_init");
		ret = pthread_create(&threads[i], NULL, thread_func, &infos[i]);
		if (ret < 0)
			pdie("creating thread %d", i);
	}

	pthread_barrier_wait(&threads_started);

	sleep(30);

	end = true;
	for (i = 0; i < nr_threads; i++) {
		pthread_mutex_lock(&infos[i].lock);
		pthread_cond_signal(&infos[i].cond);
		pthread_mutex_unlock(&infos[i].lock);
		pthread_join(threads[i], NULL);
	}

	return 0;
}

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization
  2019-12-03 10:35 ` [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization Tzvetomir Stoyanov (VMware)
  2019-12-10 17:04   ` Steven Rostedt
@ 2019-12-10 18:39   ` Steven Rostedt
  2019-12-12 12:34     ` Tzvetomir Stoyanov
  2019-12-12 14:00     ` Tzvetomir Stoyanov
  1 sibling, 2 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-10 18:39 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:20 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> The infrastructure for host - guest timestamp synchronization is divided
> in two parts:
>  - logic in libtracecmd
>  - logic in trace-cmd application
> 
> The libtracecmd is extended with new trace message, MSG_TIME_SYNC, used to
> exchange time synch information between host and guest. The trace request and
> response messages are extended to handle the time synch negotiation. The logic
> is implemented in trace-timesync.c file:
>  - Register / unregister time sync protocols.
>  - Choosing what protocol will be used for a tracing session.
>  - Init common context, used by all protocols - a ftrace instance, vsockets.
>  - Invoke protocol routines to perform time synchronization.
>  - Store calculated offsets in an array.
> 
> The trace-cmd application is extended to perform timestamp synchronization.
> The main logic is in trace-tsync.c file
>  - Negotiate what time synch algorithm will be used for the tracing session.
>  - Run pthreads, to do continuous timestamp synchronization, during the trace.
>  - Store calculated offsets in guest's trace.dat file, using TRACECMD_OPTION_TIME_SHIFT option
>  - A new trace-cmd option is added, to control the timestamp synchronization:
>       --tsync-interval: set the time sync loop interval, in ms. The default
>    value is 0 - timestamp synchronization is performed twice, before and after
>    the trace. If a negative number is specified, the timestamp synchronization
>    is disabled.
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>  Documentation/trace-cmd-record.1.txt      |   6 +
>  include/trace-cmd/trace-cmd.h             |  54 ++-
>  lib/trace-cmd/Makefile                    |   1 +
>  lib/trace-cmd/include/trace-tsync-local.h |  36 ++
>  lib/trace-cmd/trace-msg.c                 | 147 ++++++-
>  lib/trace-cmd/trace-timesync.c            | 461 ++++++++++++++++++++++
>  tracecmd/Makefile                         |   3 +-
>  tracecmd/include/trace-local.h            |  11 +
>  tracecmd/trace-agent.c                    |  34 +-
>  tracecmd/trace-record.c                   |  37 +-
>  tracecmd/trace-tsync.c                    | 268 +++++++++++++
>  tracecmd/trace-usage.c                    |   4 +
>  12 files changed, 1035 insertions(+), 27 deletions(-)
>  create mode 100644 lib/trace-cmd/include/trace-tsync-local.h
>  create mode 100644 lib/trace-cmd/trace-timesync.c
>  create mode 100644 tracecmd/trace-tsync.c
> 
> diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt
> index 0d75e43..6f09200 100644
> --- a/Documentation/trace-cmd-record.1.txt
> +++ b/Documentation/trace-cmd-record.1.txt
> @@ -338,6 +338,12 @@ OPTIONS
>      the offset will also be in nanoseconds even if the displayed units are
>      in microseconds.
>  
> +*--tsync-interval*::
> +    Set the loop interval, in ms, for timestamps synchronization with guests:
> +        If a negative number is specified, timestamps synchronization is disabled
> +        If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
> +        at the beginning and at the end of the trace\n"

We should probably state that it currently only supports VSOCK

> +
>  *--stderr*::
>      Have output go to stderr instead of stdout, but the output of the command
>      executed will not be changed. This is useful if you want to monitor the
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index 17badf2..6abcc9f 100644



>  /* --- ftrace instances --- */
>  
>  struct tracecmd_instance {
> @@ -406,6 +423,35 @@ int tracecmd_iterate_raw_events(struct tep_handle *tep,
>  				void *callback_context);
>  
>  
> +/* --- Timestamp synchronization --- */
> +
> +enum{
> +	TRACECMD_TIME_SYNC_PROTO_NONE	= 0,
> +};
> +enum{
> +	TRACECMD_TIME_SYNC_CMD_PROBE	= 1,
> +	TRACECMD_TIME_SYNC_CMD_STOP	= 2,
> +};
> +
> +#define TRACECMD_TIME_SYNC_PROTO_PTP_WEIGHT	10
> +
> +struct tracecmd_time_sync {
> +	unsigned int			sync_proto;
> +	int				loop_interval;
> +	sem_t				sem;

I already mentioned what I think about semaphores.

> +	char				*clock_str;
> +	struct tracecmd_msg_handle	*msg_handle;
> +	void				*context;
> +};
> +
> +unsigned int tracecmd_tsync_proto_getall(void);
> +unsigned int tracecmd_tsync_proto_select(unsigned int protos);
> +void tracecmd_tsync_with_host(struct tracecmd_time_sync *tsync);
> +void tracecmd_tsync_with_guest(struct tracecmd_time_sync *tsync);
> +int tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync,
> +				int *count,
> +				long long **ts, long long **offsets);
> +void tracecmd_tsync_free(struct tracecmd_time_sync *tsync);
>  
>  /* --- Plugin handling --- */
>  extern struct tep_plugin_option trace_ftrace_options[];
> diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
> index 18c7013..6221b36 100644
> --- a/lib/trace-cmd/Makefile
> +++ b/lib/trace-cmd/Makefile
> @@ -16,6 +16,7 @@ OBJS += trace-util.o
>  OBJS += trace-instance.o
>  OBJS += trace-filter-hash.o
>  OBJS += trace-msg.o
> +OBJS += trace-timesync.o
>  
>  # Additional util objects
>  OBJS += trace-blk-hack.o
> diff --git a/lib/trace-cmd/include/trace-tsync-local.h b/lib/trace-cmd/include/trace-tsync-local.h
> new file mode 100644
> index 0000000..72a6556
> --- /dev/null
> +++ b/lib/trace-cmd/include/trace-tsync-local.h
> @@ -0,0 +1,36 @@
> +/* SPDX-License-Identifier: LGPL-2.1 */
> +/*
> + * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
> + *
> + */
> +#ifndef _TRACE_TSYNC_LOCAL_H
> +#define _TRACE_TSYNC_LOCAL_H
> +
> +#include <stdbool.h>
> +

We should have a comment here that explains what the fields of the
structure are.

> +struct clock_sync_context {
> +	void				*proto_data;
> +	bool				server;
> +	struct tracecmd_instance	*vinst;
> +
> +	int				sync_size;
> +	int				sync_count;
> +	long long			*sync_ts;
> +	long long			*sync_offsets;
> +
> +	unsigned int			local_cid;
> +	unsigned int			local_port;
> +	unsigned int			remote_cid;
> +	unsigned int			remote_port;
> +};
> +
> +int tracecmd_tsync_proto_register(unsigned int proto_id, int weight,
> +				int (*init)(struct tracecmd_time_sync *),
> +				int (*free)(struct tracecmd_time_sync *),
> +				int (*calc)(struct tracecmd_time_sync *,
> +					    long long *, long long *));
> +int tracecmd_tsync_proto_unregister(unsigned int proto_id);
> +
> +int ptp_clock_sync_register(void);
> +
> +#endif /* _TRACE_TSYNC_LOCAL_H */
> diff --git a/lib/trace-cmd/trace-msg.c b/lib/trace-cmd/trace-msg.c
> index a34297f..b6ea5fc 100644
> --- a/lib/trace-cmd/trace-msg.c
> +++ b/lib/trace-cmd/trace-msg.c
> @@ -28,6 +28,8 @@
>  
>  typedef __u32 u32;
>  typedef __be32 be32;
> +typedef __u64 u64;
> +typedef __s64 s64;
>  
>  static inline void dprint(const char *fmt, ...)
>  {
> @@ -64,6 +66,7 @@ struct tracecmd_msg_trace_req {
>  	be32 flags;
>  	be32 argc;
>  	u64 trace_id;
> +	be32 tsync_protos;
>  } __attribute__((packed));
>  
>  struct tracecmd_msg_trace_resp {
> @@ -71,6 +74,13 @@ struct tracecmd_msg_trace_resp {
>  	be32 cpus;
>  	be32 page_size;
>  	u64 trace_id;
> +	be32 tsync_proto;
> +	be32 tsync_port;
> +} __attribute__((packed));
> +
> +struct tracecmd_msg_tsync {
> +	be32 sync_protocol;
> +	be32 sync_msg_id;
>  } __attribute__((packed));
>  
>  struct tracecmd_msg_header {
> @@ -88,7 +98,8 @@ struct tracecmd_msg_header {
>  	C(NOT_SUPP,	5,	0),					\
>  	C(TRACE_REQ,	6,	sizeof(struct tracecmd_msg_trace_req)),	\
>  	C(TRACE_RESP,	7,	sizeof(struct tracecmd_msg_trace_resp)),\
> -	C(CLOSE_RESP,	8,	0),
> +	C(CLOSE_RESP,	8,	0),					\
> +	C(TIME_SYNC,	9,	sizeof(struct tracecmd_msg_tsync)),
>  
>  #undef C
>  #define C(a,b,c)	MSG_##a = b
> @@ -122,6 +133,7 @@ struct tracecmd_msg {
>  		struct tracecmd_msg_rinit	rinit;
>  		struct tracecmd_msg_trace_req	trace_req;
>  		struct tracecmd_msg_trace_resp	trace_resp;
> +		struct tracecmd_msg_tsync	tsync;
>  	};
>  	char					*buf;
>  } __attribute__((packed));
> @@ -264,6 +276,17 @@ static int tracecmd_msg_send(int fd, struct tracecmd_msg *msg)
>  	return ret;
>  }
>  
> +static int tracecmd_msg_send_nofree(int fd, struct tracecmd_msg *msg)

We probably shouldn't have a static function that starts with
"tracecmd_".

> +{
> +	int ret = 0;
> +
> +	ret = msg_write(fd, msg);
> +	if (ret < 0)
> +		ret = -ECOMM;
> +
> +	return ret;
> +}
> +
>  static int msg_read(int fd, void *buf, u32 size, int *n)
>  {
>  	ssize_t r;
> @@ -814,7 +837,8 @@ int tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle)
>  }
>  
>  static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
> -			  bool use_fifos, unsigned long long trace_id)
> +			  bool use_fifos, unsigned long long trace_id,
> +			  unsigned int tsync_protos)
>  {
>  	size_t args_size = 0;
>  	char *p;
> @@ -824,7 +848,11 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
>  		args_size += strlen(argv[i]) + 1;
>  
>  	msg->hdr.size = htonl(ntohl(msg->hdr.size) + args_size);
> -	msg->trace_req.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
> +	msg->trace_req.flags = 0;
> +	if (use_fifos)
> +		msg->trace_req.flags |= MSG_TRACE_USE_FIFOS;

Why the change to flags here?

> +	msg->trace_req.flags = htonl(msg->trace_req.flags);
> +	msg->trace_req.tsync_protos = htonl(tsync_protos);
>  	msg->trace_req.argc = htonl(argc);
>  	msg->trace_req.trace_id = htonll(trace_id);
>  	msg->buf = calloc(args_size, 1);
> @@ -840,28 +868,30 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
>  
>  int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
>  				int argc, char **argv, bool use_fifos,
> -				unsigned long long trace_id)
> +				unsigned long long trace_id,
> +				unsigned int tsync_protos)
>  {
>  	struct tracecmd_msg msg;
>  	int ret;
>  
>  	tracecmd_msg_init(MSG_TRACE_REQ, &msg);
>  	ret = make_trace_req(&msg, argc, argv,
> -			     use_fifos, trace_id);
> +			     use_fifos, trace_id, tsync_protos);
>  	if (ret < 0)
>  		return ret;
>  
>  	return tracecmd_msg_send(msg_handle->fd, &msg);
>  }
>  
> - /*
> -  * NOTE: On success, the returned `argv` should be freed with:
> -  *     free(argv[0]);
> -  *     free(argv);
> -  */
> +/*
> + * NOTE: On success, the returned `argv` should be freed with:
> + *     free(argv[0]);
> + *     free(argv);
> + */
>  int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
>  				int *argc, char ***argv, bool *use_fifos,
> -				unsigned long long *trace_id)
> +				unsigned long long *trace_id,
> +				unsigned int *tsync_protos)
>  {
>  	struct tracecmd_msg msg;
>  	char *p, *buf_end, **args;
> @@ -908,6 +938,7 @@ int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
>  	*argc = nr_args;
>  	*argv = args;
>  	*use_fifos = ntohl(msg.trace_req.flags) & MSG_TRACE_USE_FIFOS;
> +	*tsync_protos = ntohl(msg.trace_req.tsync_protos);
>  	*trace_id = ntohll(msg.trace_req.trace_id);
>  
>  	/*
> @@ -928,9 +959,81 @@ out:
>  	return ret;
>  }
>  

Should add a kerneldoc comment that describes this function. If for
anything else, to describe the parameters.

> +int tracecmd_msg_send_time_sync(struct tracecmd_msg_handle *msg_handle,
> +				unsigned int sync_protocol,
> +				unsigned int sync_msg_id,
> +				unsigned int payload_size, char *payload)
> +{
> +	struct tracecmd_msg msg;
> +
> +	tracecmd_msg_init(MSG_TIME_SYNC, &msg);
> +	msg.tsync.sync_protocol = htonl(sync_protocol);
> +	msg.tsync.sync_msg_id = htonl(sync_msg_id);
> +	msg.hdr.size = htonl(ntohl(msg.hdr.size) + payload_size);
> +
> +	msg.buf = payload;
> +	return tracecmd_msg_send_nofree(msg_handle->fd, &msg);
> +}
> +

Should add a kerneldoc comment for this function too.

> +int tracecmd_msg_recv_time_sync(struct tracecmd_msg_handle *msg_handle,
> +				unsigned int *sync_protocol,
> +				unsigned int *sync_msg_id,
> +				unsigned int *payload_size, char **payload)
> +{
> +	struct tracecmd_msg msg;
> +	int ret = -1;
> +	int buf_size;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	ret = tracecmd_msg_recv(msg_handle->fd, &msg);
> +	if (ret < 0)
> +		goto out;
> +
> +	if (ntohl(msg.hdr.cmd) != MSG_TIME_SYNC) {
> +		ret = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	if (sync_protocol)
> +		*sync_protocol = ntohl(msg.tsync.sync_protocol);
> +	if (sync_msg_id)
> +		*sync_msg_id = ntohl(msg.tsync.sync_msg_id);
> +
> +	buf_size = msg_buf_len(&msg);
> +	if (buf_size < 0) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (buf_size && payload && payload_size) {
> +		if (*payload_size) {
> +			if (*payload_size != buf_size || *payload == NULL) {

This patch doesn't yet have a use case for payload_size, so I don't
know how its used. But I'm not sure we should make it have to equal
buf_size, perhaps we should have it be:

			*payload_size < buf_size


As it should be OK to over allocate, but not under allocate.


> +				ret = -ENOMEM;
> +				goto out;
> +			}
> +			memcpy(*payload, msg.buf, buf_size);
> +			goto out;
> +		}
> +
> +		*payload = malloc(buf_size);
> +		if (*payload == NULL) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +		*payload_size = buf_size;
> +		memcpy(*payload, msg.buf, buf_size);
> +	}
> +
> +out:
> +	msg_free(&msg);
> +	return ret;
> +}
> +
>  static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
>  			   unsigned int *ports, bool use_fifos,
> -			   unsigned long long trace_id)
> +			   unsigned long long trace_id,
> +			   unsigned int tsync_proto,
> +			   unsigned int tsync_port)
>  {
>  	int data_size;
>  
> @@ -941,7 +1044,13 @@ static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
>  	write_uints(msg->buf, data_size, ports, nr_cpus);
>  
>  	msg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);
> -	msg->trace_resp.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
> +	msg->trace_resp.flags = 0;
> +	if (use_fifos)
> +		msg->trace_resp.flags |= MSG_TRACE_USE_FIFOS;
> +	msg->trace_resp.flags = htonl(msg->trace_resp.flags);
> +	msg->trace_resp.tsync_proto = htonl(tsync_proto);
> +	msg->trace_resp.tsync_port = htonl(tsync_port);
> +
>  	msg->trace_resp.cpus = htonl(nr_cpus);
>  	msg->trace_resp.page_size = htonl(page_size);
>  	msg->trace_resp.trace_id = htonll(trace_id);
> @@ -952,14 +1061,16 @@ static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
>  int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  				 int nr_cpus, int page_size,
>  				 unsigned int *ports, bool use_fifos,
> -				 unsigned long long trace_id)
> +				 unsigned long long trace_id,
> +				 unsigned int tsync_proto,
> +				 unsigned int tsync_port)
>  {
>  	struct tracecmd_msg msg;
>  	int ret;
>  
>  	tracecmd_msg_init(MSG_TRACE_RESP, &msg);
>  	ret = make_trace_resp(&msg, page_size, nr_cpus, ports,
> -			      use_fifos, trace_id);
> +			      use_fifos, trace_id, tsync_proto, tsync_port);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -969,7 +1080,9 @@ int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  				 int *nr_cpus, int *page_size,
>  				 unsigned int **ports, bool *use_fifos,
> -				 unsigned long long *trace_id)
> +				 unsigned long long *trace_id,
> +				 unsigned int *tsync_proto,
> +				 unsigned int *tsync_port)
>  {
>  	struct tracecmd_msg msg;
>  	char *p, *buf_end;
> @@ -996,6 +1109,8 @@ int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  	*nr_cpus = ntohl(msg.trace_resp.cpus);
>  	*page_size = ntohl(msg.trace_resp.page_size);
>  	*trace_id = ntohll(msg.trace_resp.trace_id);
> +	*tsync_proto = ntohl(msg.trace_resp.tsync_proto);
> +	*tsync_port = ntohl(msg.trace_resp.tsync_port);
>  	*ports = calloc(*nr_cpus, sizeof(**ports));
>  	if (!*ports) {
>  		ret = -ENOMEM;
> diff --git a/lib/trace-cmd/trace-timesync.c b/lib/trace-cmd/trace-timesync.c
> new file mode 100644
> index 0000000..073423d
> --- /dev/null
> +++ b/lib/trace-cmd/trace-timesync.c
> @@ -0,0 +1,461 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +/*
> + * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
> + *
> + */
> +
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <arpa/inet.h>
> +#include <linux/vm_sockets.h>
> +#include <linux/limits.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <time.h>
> +#include <dirent.h>
> +#include <errno.h>
> +#include "trace-cmd.h"
> +#include "trace-cmd-local.h"
> +#include "trace-tsync-local.h"
> +
> +struct tsync_proto {
> +	struct tsync_proto *next;
> +	unsigned int proto_id;
> +	int	weight;
> +
> +	int (*clock_sync_init)(struct tracecmd_time_sync *clock_context);
> +	int (*clock_sync_free)(struct tracecmd_time_sync *clock_context);
> +	int (*clock_sync_calc)(struct tracecmd_time_sync *clock_context,
> +			       long long *offset, long long *timestamp);
> +} static * tsync_proto_list;

Just a nit, but it's usually cleaner and easier to read to separate the
struct declaration and the variables. That is:

struct tsync_proto {
	[..]
};

static struct tsync_proto *tsync_proto_list;

> +
> +static struct tsync_proto *tsync_proto_get(unsigned int proto_id)

I'd rename this to tsync_find_proto(), as to me, "_get" usually means
that a reference count is incremented and it should be paired with a
"_put" somewhere.


> +{
> +	struct tsync_proto *proto = tsync_proto_list;
> +
> +	while (proto) {
> +		if (proto->proto_id == proto_id)
> +			return proto;
> +		proto = proto->next;
> +	}

Another nit, but I usually prefer for loops for this ;-)

	for (proto = tsync_proto_list; proto; proto = proto->next) {
		if (proto->proto_id == proto_id)
			return proto;
	}



> +	return NULL;
> +}
> +
> +int tracecmd_tsync_proto_register(unsigned int proto_id, int weight,
> +				int (*init)(struct tracecmd_time_sync *),
> +				int (*free)(struct tracecmd_time_sync *),
> +				int (*calc)(struct tracecmd_time_sync *,
> +					    long long *, long long *))
> +{
> +	struct tsync_proto *proto;
> +
> +	if (tsync_proto_get(proto_id))
> +		return -1;
> +	proto = calloc(1, sizeof(struct tsync_proto));
> +	if (!proto)
> +		return -1;
> +	proto->proto_id = proto_id;
> +	proto->weight = weight;
> +	proto->clock_sync_init = init;
> +	proto->clock_sync_free = free;
> +	proto->clock_sync_calc = calc;
> +
> +	proto->next = tsync_proto_list;
> +	tsync_proto_list = proto;
> +	return 0;
> +}
> +
> +int tracecmd_tsync_proto_unregister(unsigned int proto_id)
> +{
> +	struct tsync_proto *proto = tsync_proto_list;
> +	struct tsync_proto *pdel;
> +
> +	if (tsync_proto_list && tsync_proto_list->proto_id == proto_id) {
> +		pdel = tsync_proto_list;
> +		tsync_proto_list = pdel->next;
> +		free(pdel);
> +		return 0;
> +	}
> +
> +	while (proto->next) {
> +		if (proto->next->proto_id == proto_id) {
> +			pdel = proto->next;
> +			proto->next = pdel->next;
> +			free(pdel);
> +			return 0;
> +		}
> +		proto = proto->next;
> +	}

The above could be written as one loop:


	struct tsync_proto **last = &tsync_proto_list;

	for (; *last; last = &(*last)->next) {
		if ((*last)->proto_id = proto_id) {
			struct tsync_proto *proto = *last;

			*last = proto->next;
			free(proto);
			return 0;
		}
	}


> +
> +	return -1;
> +}
> +
> +/**
> + * tracecmd_tsync_get_offsets - Return the calculated time offsests
> + *
> + * @tsync: Pointer to time sync context.
> + * @count: Returns the number of calculated time offsets.
> + * @ts: Array of size @count containing timestamps of callculated offsets
> + * @offsets: array of size @count, containing offsets for each timestamp.
> + *
> + * Retuns -1 in case of an error, or 0 otherwise
> + */
> +int tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync,
> +				int *count,
> +				long long **ts, long long **offsets)
> +{
> +	struct clock_sync_context *tsync_context;
> +
> +	if (!tsync || !tsync->context)
> +		return -1;
> +	tsync_context = (struct clock_sync_context *)tsync->context;
> +	if (count)
> +		*count = tsync_context->sync_count;
> +	if (ts)
> +		*ts = tsync_context->sync_ts;
> +	if (offsets)
> +		*offsets = tsync_context->sync_offsets;
> +	return 0;
> +}
> +
> +/**
> + * tracecmd_tsync_proto_select - Select time sync protocol, to be used for
> + *		timestamp synchronization with a peer.
> + *
> + * @protos: Bitmask of time sync protocols, supported by the peer.

Hmm, so we are limiting the number of protocols that we will ever
support to 32? This makes me nervous (I always think of the famous
quote of "Who will ever need more than 64K of RAM?")

To be safe, we should probably make this an array:

	int tracecmd_tsync_proto_select(unsigned int *protos, int words)

The proto_id should be a bit, not a mask, that is:

	int word;

	for (word = 0; word < words; word++) {

		for (proto = tsync_proto_list; proto; proto = proto->next) {
			if (proto->proto_id < word * 32)
				continue;

			id = proto->proto_id - word * 32;
			if (id >= 32)
				continue;

			if ((1 << id) & protos[word]) {
				if (selected)
					[...]

While we only have a single word, callers would just do:

	unsigned int protos;

	ret = tracecmd_tsync_proto_select(&protos, 1);

At least this will never limit us to just 32 protocols, even though we
will likely never need more. But we are safe if we ever do ;-)

> + *
> + * Retuns Id of a time sync protocol, that can be used with the peer, or 0
> + *	in case there is no match with supported protocols.
> + */
> +unsigned int tracecmd_tsync_proto_select(unsigned int protos)
> +{
> +	struct tsync_proto *proto = tsync_proto_list;
> +	struct tsync_proto *selected = NULL;
> +
> +	while (proto) {
> +		if (proto->proto_id & protos) {
> +			if (selected) {
> +				if (selected->weight < proto->weight)
> +					selected = proto;
> +			} else
> +				selected = proto;
> +		}
> +		proto = proto->next;
> +	}
> +
> +	if (selected)
> +		return selected->proto_id;
> +
> +	return 0;
> +}
> +
> +/**
> + * tracecmd_tsync_proto_getall - Returns bitmask of all supported
> + *				 time sync protocols.
> + *
> + */
> +unsigned int tracecmd_tsync_proto_getall(void)

With the bitmask I suggest above, this would need to pass back as
allocate mask.

> +{
> +	struct tsync_proto *proto = tsync_proto_list;
> +	unsigned int protos = 0;
> +
> +	while (proto) {
> +		protos |= proto->proto_id;
> +		proto = proto->next;
> +	}
> +
> +	return protos;
> +}
> +
> +static int get_vsocket_params(int fd, unsigned int *lcid, unsigned int *lport,
> +			      unsigned int *rcid, unsigned int *rport)
> +{
> +	struct sockaddr_vm addr;
> +	socklen_t addr_len = sizeof(addr);
> +
> +	memset(&addr, 0, sizeof(addr));
> +	if (getsockname(fd, (struct sockaddr *)&addr, &addr_len))
> +		return -1;
> +	if (addr.svm_family != AF_VSOCK)
> +		return -1;
> +	*lport = addr.svm_port;
> +	*lcid = addr.svm_cid;
> +
> +	memset(&addr, 0, sizeof(addr));
> +	addr_len = sizeof(addr);
> +	if (getpeername(fd, (struct sockaddr *)&addr, &addr_len))
> +		return -1;
> +	if (addr.svm_family != AF_VSOCK)
> +		return -1;
> +	*rport = addr.svm_port;
> +	*rcid = addr.svm_cid;
> +
> +	return 0;
> +}
> +
> +static struct tracecmd_instance *
> +clock_synch_create_instance(const char *clock, unsigned int cid)
> +{
> +	struct tracecmd_instance *vinst;
> +	char inst_name[256];
> +
> +	snprintf(inst_name, 256, "clock_synch-%d", cid);
> +
> +	vinst = tracecmd_create_instance(inst_name);
> +	if (!vinst)
> +		return NULL;
> +
> +	tracecmd_make_instance(vinst);
> +	tracecmd_write_instance_file(vinst, "trace", "\0", NULL);
> +	if (clock)
> +		vinst->clock = strdup(clock);
> +	tracecmd_set_clock(vinst, NULL);
> +	return vinst;
> +}
> +
> +static void
> +clock_synch_delete_instance(struct tracecmd_instance *inst)
> +{
> +	if (!inst)
> +		return;
> +	tracecmd_remove_instance(inst);
> +	free(inst->clock);
> +	tracecmd_free_instance(inst);
> +}
> +
> +static int clock_context_init(struct tracecmd_time_sync *tsync, bool server)
> +{
> +	struct clock_sync_context *clock = NULL;
> +	struct tsync_proto *protocol;
> +
> +	if (tsync->context)
> +		return 0;
> +
> +	protocol = tsync_proto_get(tsync->sync_proto);
> +	if (!protocol)
> +		return -1;
> +
> +	clock = calloc(1, sizeof(struct clock_sync_context));
> +	if (!clock)
> +		return -1;
> +
> +	clock->server = server;
> +	if (get_vsocket_params(tsync->msg_handle->fd, &clock->local_cid,
> +			       &clock->local_port, &clock->remote_cid,
> +			       &clock->remote_port))
> +		goto error;
> +
> +	clock->vinst = clock_synch_create_instance(tsync->clock_str,
> +						   clock->remote_cid);
> +	if (!clock->vinst)
> +		goto error;
> +
> +	tsync->context = clock;
> +	if (protocol->clock_sync_init && protocol->clock_sync_init(tsync) < 0)
> +		goto error;
> +
> +	return 0;
> +error:
> +	tsync->context = NULL;
> +	free(clock);
> +	return -1;
> +}
> +
> +/**
> + * tracecmd_tsync_free - Free time sync context, allocated by
> + *		tracecmd_tsync_with_host() or tracecmd_tsync_with_guest() APIs
> + *
> + * @tsync: Pointer to time sync context.
> + *
> + */
> +void tracecmd_tsync_free(struct tracecmd_time_sync *tsync)
> +{
> +	struct clock_sync_context *tsync_context;
> +	struct tsync_proto *proto;
> +
> +	if (!tsync->context)
> +		return;
> +	tsync_context = (struct clock_sync_context *)tsync->context;
> +
> +	proto = tsync_proto_get(tsync->sync_proto);
> +	if (proto && proto->clock_sync_free)
> +		proto->clock_sync_free(tsync);
> +
> +	clock_synch_delete_instance(tsync_context->vinst);
> +	tsync_context->vinst = NULL;
> +
> +	free(tsync_context->sync_ts);
> +	free(tsync_context->sync_offsets);
> +	tsync_context->sync_ts = NULL;
> +	tsync_context->sync_offsets = NULL;
> +	tsync_context->sync_count = 0;
> +	tsync_context->sync_size = 0;
> +	sem_destroy(&tsync->sem);
> +	free(tsync->clock_str);
> +}
> +
> +static int tracecmd_tsync_send(struct tracecmd_time_sync *tsync,
> +				  struct tsync_proto *proto)
> +{
> +	long long timestamp = 0;
> +	long long offset = 0;
> +	int ret;
> +
> +	ret = proto->clock_sync_calc(tsync, &offset, &timestamp);
> +
> +	return ret;
> +}
> +
> +/**
> + * tracecmd_tsync_with_host - Synchronize timestamps with host
> + *
> + * @tsync: Pointer to time sync context.
> + *
> + * This API is supposed to be called in guest context. It waits for a time
> + * sync request from the host and replies with a time sample, until time sync
> + * stop command is received.
> + *
> + */
> +void tracecmd_tsync_with_host(struct tracecmd_time_sync *tsync)
> +{
> +	struct tsync_proto *proto;
> +	unsigned int protocol;
> +	unsigned int command;
> +	int ret;
> +
> +	proto = tsync_proto_get(tsync->sync_proto);
> +	if (!proto || !proto->clock_sync_calc)
> +		return;
> +
> +	clock_context_init(tsync, true);
> +	if (!tsync->context)
> +		return;
> +
> +	while (true) {
> +		ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
> +						  &protocol, &command,
> +						  NULL, NULL);
> +
> +		if (ret ||
> +		    protocol != TRACECMD_TIME_SYNC_PROTO_NONE ||
> +		    command != TRACECMD_TIME_SYNC_CMD_PROBE)
> +			break;
> +		ret = tracecmd_tsync_send(tsync, proto);
> +		if (ret)
> +			break;
> +	}
> +}
> +
> +static int tracecmd_tsync_get(struct tracecmd_time_sync *tsync,
> +			      struct tsync_proto *proto,
> +			      int array_step)
> +{
> +	struct clock_sync_context *clock;
> +	long long *sync_offsets = NULL;
> +	long long *sync_ts = NULL;
> +	long long timestamp = 0;
> +	long long offset = 0;
> +	int ret;
> +
> +	ret = proto->clock_sync_calc(tsync, &offset, &timestamp);
> +	if (ret) {
> +		warning("Failed to synchronize timestamps with guest");
> +		return -1;
> +	}
> +	if (!offset || !timestamp)
> +		return 0;
> +	clock = tsync->context;
> +	if (clock->sync_count >= clock->sync_size) {
> +		sync_ts = realloc(clock->sync_ts,
> +				  (clock->sync_size + array_step) * sizeof(long long));
> +		sync_offsets = realloc(clock->sync_offsets,
> +				       (clock->sync_size + array_step) * sizeof(long long));
> +		if (!sync_ts || !sync_offsets) {
> +			free(sync_ts);
> +			free(sync_offsets);
> +			return -1;
> +		}
> +		clock->sync_size += array_step;
> +		clock->sync_ts = sync_ts;
> +		clock->sync_offsets = sync_offsets;
> +	}
> +
> +	clock->sync_ts[clock->sync_count] = timestamp;
> +	clock->sync_offsets[clock->sync_count] = offset;
> +	clock->sync_count++;
> +
> +	return 0;
> +}
> +
> +#define TIMER_SEC_NANO 1000000000LL
> +static inline void get_ts_loop_delay(struct timespec *timeout, int delay_ms)
> +{
> +	memset(timeout, 0, sizeof(struct timespec));
> +	clock_gettime(CLOCK_REALTIME, timeout);
> +
> +	timeout->tv_nsec += ((unsigned long long)delay_ms * 1000000LL);
> +
> +	if (timeout->tv_nsec >= TIMER_SEC_NANO) {
> +		timeout->tv_sec += timeout->tv_nsec / TIMER_SEC_NANO;
> +		timeout->tv_nsec %= TIMER_SEC_NANO;
> +	}
> +}
> +
> +#define CLOCK_TS_ARRAY 5
> +/**
> + * tracecmd_tsync_with_guest - Synchronize timestamps with guest
> + *
> + * @tsync: Pointer to time sync context.
> + *
> + * This API is supposed to be called in host context, in a separate thread.
> + * It loops infinite, until the timesync semaphore is released.
> + *
> + */
> +void tracecmd_tsync_with_guest(struct tracecmd_time_sync *tsync)
> +{
> +	int ts_array_size = CLOCK_TS_ARRAY;
> +	struct tsync_proto *proto;
> +	struct timespec timeout;
> +	bool end = false;
> +	int ret;
> +
> +	proto = tsync_proto_get(tsync->sync_proto);
> +	if (!proto || !proto->clock_sync_calc)
> +		return;
> +
> +	clock_context_init(tsync, false);
> +	if (!tsync->context)
> +		return;
> +
> +	if (tsync->loop_interval > 0 &&
> +	    tsync->loop_interval < (CLOCK_TS_ARRAY * 1000))
> +		ts_array_size = (CLOCK_TS_ARRAY * 1000) / tsync->loop_interval;
> +
> +	while (true) {
> +		ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
> +						  TRACECMD_TIME_SYNC_PROTO_NONE,
> +						  TRACECMD_TIME_SYNC_CMD_PROBE,
> +						  0, NULL);
> +		ret = tracecmd_tsync_get(tsync, proto, ts_array_size);
> +		if (ret || end)
> +			break;
> +		if (tsync->loop_interval > 0) {
> +			get_ts_loop_delay(&timeout, tsync->loop_interval);
> +			ret = sem_timedwait(&tsync->sem, &timeout);
> +			if (ret < 0) {
> +				if (errno != ETIMEDOUT && errno != EINTR)
> +					break;
> +			} else if (!ret)
> +				end = true;
> +		} else {
> +			sem_wait(&tsync->sem);
> +			end = true;
> +		}
> +	};
> +
> +	tracecmd_msg_send_time_sync(tsync->msg_handle,
> +				    TRACECMD_TIME_SYNC_PROTO_NONE,
> +				    TRACECMD_TIME_SYNC_CMD_STOP,
> +				    0, NULL);
> +}
> diff --git a/tracecmd/Makefile b/tracecmd/Makefile
> index 29a623b..908dab2 100644
> --- a/tracecmd/Makefile
> +++ b/tracecmd/Makefile
> @@ -30,6 +30,7 @@ TRACE_CMD_OBJS += trace-check-events.o
>  TRACE_CMD_OBJS += trace-show.o
>  TRACE_CMD_OBJS += trace-list.o
>  TRACE_CMD_OBJS += trace-usage.o
> +TRACE_CMD_OBJS += trace-tsync.o
>  
>  ifeq ($(VSOCK_DEFINED), 1)
>  TRACE_CMD_OBJS += trace-agent.o
> @@ -42,7 +43,7 @@ all_objs := $(sort $(ALL_OBJS))
>  all_deps := $(all_objs:$(bdir)/%.o=$(bdir)/.%.d)
>  
>  CONFIG_INCLUDES =
> -CONFIG_LIBS	= -lrt
> +CONFIG_LIBS	= -lrt -lpthread
>  CONFIG_FLAGS	=
>  
>  all: $(TARGETS)
> diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
> index 17ef31a..d652317 100644
> --- a/tracecmd/include/trace-local.h
> +++ b/tracecmd/include/trace-local.h
> @@ -226,6 +226,10 @@ struct buffer_instance {
>  	unsigned int		port;
>  	int			*fds;
>  	bool			use_fifos;
> +
> +	pthread_t		tsync_thread;
> +	bool			tsync_thread_running;
> +	struct tracecmd_time_sync tsync;
>  };
>  
>  void init_top_instance(void);
> @@ -260,6 +264,13 @@ void tracecmd_disable_tracing(void);
>  void tracecmd_enable_tracing(void);
>  void tracecmd_stat_cpu(struct trace_seq *s, int cpu);
>  
> +int tracecmd_host_tsync(struct buffer_instance *instance,
> +			 unsigned int tsync_port);
> +void tracecmd_host_tsync_complete(struct buffer_instance *instance);
> +unsigned int tracecmd_guest_tsync(unsigned int tsync_protos, char *clock,
> +				  unsigned int *tsync_port,
> +				  pthread_t *thr_id);
> +
>  int make_vsock(unsigned int port);
>  int get_vsock_port(int sd, unsigned int *port);
>  int open_vsock(unsigned int cid, unsigned int port);
> diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
> index 530f972..1581de8 100644
> --- a/tracecmd/trace-agent.c
> +++ b/tracecmd/trace-agent.c
> @@ -19,6 +19,7 @@
>  #include <sys/wait.h>
>  #include <unistd.h>
>  #include <linux/vm_sockets.h>
> +#include <pthread.h>
>  
>  #include "trace-local.h"
>  #include "trace-msg.h"
> @@ -125,11 +126,28 @@ cleanup:
>  	return ret;
>  }
>  
> +static char *get_clock(int argc, char **argv)
> +{
> +	int i;
> +
> +	if (!argc || !argv)
> +		return NULL;
> +
> +	for (i = 0; i < argc - 1; i++) {
> +		if (!strcmp("-C", argv[i]))
> +			return argv[i+1];
> +	}
> +	return NULL;
> +}
> +
>  static void agent_handle(int sd, int nr_cpus, int page_size)
>  {
>  	struct tracecmd_msg_handle *msg_handle;
>  	unsigned long long trace_id;
> +	unsigned int tsync_protos = 0;
> +	unsigned int tsync_port = 0;
>  	unsigned int *ports;
> +	pthread_t sync_thr;
>  	char **argv = NULL;
>  	int argc = 0;
>  	bool use_fifos;
> @@ -146,7 +164,8 @@ static void agent_handle(int sd, int nr_cpus, int page_size)
>  		die("Failed to allocate message handle");
>  
>  	ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
> -					  &use_fifos, &trace_id);
> +					  &use_fifos, &trace_id,
> +					  &tsync_protos);
>  	if (ret < 0)
>  		die("Failed to receive trace request");
>  
> @@ -155,14 +174,25 @@ static void agent_handle(int sd, int nr_cpus, int page_size)
>  
>  	if (!use_fifos)
>  		make_vsocks(nr_cpus, fds, ports);
> +	if (tsync_protos) {
> +		tsync_protos = tracecmd_guest_tsync(tsync_protos,
> +						    get_clock(argc, argv),
> +						    &tsync_port, &sync_thr);
> +		if (!tsync_protos)
> +			warning("Failed to negotiate timestamps synchronization with the host");
> +	}
>  	trace_id = tracecmd_generate_traceid();
>  	ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
> -					   ports, use_fifos, trace_id);
> +					   ports, use_fifos, trace_id,
> +					   tsync_protos, tsync_port);
>  	if (ret < 0)
>  		die("Failed to send trace response");
>  	trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
>  			   use_fifos, trace_id);
>  
> +	if (tsync_protos)
> +		pthread_join(sync_thr, NULL);
> +
>  	free(argv[0]);
>  	free(argv);
>  	free(ports);
> diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
> index e66f999..e543c40 100644
> --- a/tracecmd/trace-record.c
> +++ b/tracecmd/trace-record.c
> @@ -670,6 +670,11 @@ static void tell_guests_to_stop(void)
>  			tracecmd_msg_send_close_msg(instance->msg_handle);
>  	}
>  
> +	for_all_instances(instance) {
> +		if (is_guest(instance))
> +			tracecmd_host_tsync_complete(instance);
> +	}
> +
>  	/* Wait for guests to acknowledge */
>  	for_all_instances(instance) {
>  		if (is_guest(instance)) {
> @@ -3501,6 +3506,8 @@ static void connect_to_agent(struct buffer_instance *instance)
>  {
>  	struct tracecmd_msg_handle *msg_handle;
>  	int sd, ret, nr_fifos, nr_cpus, page_size;
> +	unsigned int tsync_protos_reply = 0;
> +	unsigned int tsync_port = 0;
>  	unsigned int *ports;
>  	int i, *fds = NULL;
>  	bool use_fifos = false;
> @@ -3519,19 +3526,32 @@ static void connect_to_agent(struct buffer_instance *instance)
>  	if (!msg_handle)
>  		die("Failed to allocate message handle");
>  
> +	if (instance->tsync.loop_interval >= 0)
> +		instance->tsync.sync_proto = tracecmd_tsync_proto_getall();
> +
>  	ret = tracecmd_msg_send_trace_req(msg_handle, instance->argc,
>  					  instance->argv, use_fifos,
> -					  top_instance.trace_id);
> +					  top_instance.trace_id,
> +					  instance->tsync.sync_proto);
>  
>  	if (ret < 0)
>  		die("Failed to send trace request");
>  
>  	ret = tracecmd_msg_recv_trace_resp(msg_handle, &nr_cpus, &page_size,
>  					   &ports, &use_fifos,
> -					   &instance->trace_id);
> +					   &instance->trace_id,
> +					   &tsync_protos_reply, &tsync_port);
>  
>  	if (ret < 0)
> -		die("Failed to receive trace response");
> +		die("Failed to receive trace response %d", ret);
> +
> +	if (instance->tsync.sync_proto) {
> +		if (instance->tsync.sync_proto & tsync_protos_reply) {
> +			instance->tsync.sync_proto = tsync_protos_reply;
> +			tracecmd_host_tsync(instance, tsync_port);
> +		} else
> +			warning("Failed to negotiate timestamps synchronization with the guest");
> +	}
>  
>  	if (use_fifos) {
>  		if (nr_cpus != nr_fifos) {
> @@ -3578,7 +3598,9 @@ static void setup_guest(struct buffer_instance *instance)
>  	close(fd);
>  }
>  
> -static void setup_agent(struct buffer_instance *instance, struct common_record_context *ctx)
> +static void setup_agent(struct buffer_instance *instance,
> +			struct common_record_context *ctx)
> +
>  {
>  	struct tracecmd_output *network_handle;
>  
> @@ -5131,6 +5153,7 @@ void init_top_instance(void)
>  }
>  
>  enum {
> +	OPT_tsyncinterval	= 242,
>  	OPT_user		= 243,
>  	OPT_procmap		= 244,
>  	OPT_quiet		= 245,
> @@ -5433,6 +5456,7 @@ static void parse_record_options(int argc,
>  			{"proc-map", no_argument, NULL, OPT_procmap},
>  			{"user", required_argument, NULL, OPT_user},
>  			{"module", required_argument, NULL, OPT_module},
> +			{"tsync-interval", required_argument, NULL, OPT_tsyncinterval},
>  			{NULL, 0, NULL, 0}
>  		};
>  
> @@ -5771,6 +5795,10 @@ static void parse_record_options(int argc,
>  			ctx->instance->filter_mod = optarg;
>  			ctx->filtered = 0;
>  			break;
> +		case OPT_tsyncinterval:
> +			top_instance.tsync.loop_interval = atoi(optarg);
> +			guest_config = true;

I still don't like the name of the variable. "guest_config" makes it
sound like this is a guest config, or we set a guest config. Where as
you are just going to do something to the guests when this gets set.
It's still rather confusing.

Still would be better with "clock_option_set" or something like that.
It's more descriptive of why it is true. Or perhaps something like
"guest_sync_set", where we are saying that we need to do something for
synchronizing guests.


> +			break;
>  		case OPT_quiet:
>  		case 'q':
>  			quiet = true;
> @@ -5799,6 +5827,7 @@ static void parse_record_options(int argc,
>  					add_argv(instance, "-C", true);
>  				}
>  			}
> +		instance->tsync.loop_interval = top_instance.tsync.loop_interval;
>  		}
>  	}
>  
> diff --git a/tracecmd/trace-tsync.c b/tracecmd/trace-tsync.c
> new file mode 100644
> index 0000000..e4189e0
> --- /dev/null
> +++ b/tracecmd/trace-tsync.c
> @@ -0,0 +1,268 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
> + *
> + */
> +#include <stdlib.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <sys/socket.h>
> +#include <unistd.h>
> +#include <linux/vm_sockets.h>
> +#include <pthread.h>
> +
> +#include "trace-local.h"
> +#include "trace-msg.h"
> +
> +static int get_first_cpu(cpu_set_t **pin_mask, size_t *m_size)
> +{
> +	int cpus = tracecmd_count_cpus();
> +	cpu_set_t *cpu_mask;
> +	int mask_size;
> +	int i;
> +
> +	cpu_mask = CPU_ALLOC(cpus);
> +	*pin_mask = CPU_ALLOC(cpus);
> +	if (!cpu_mask || !*pin_mask || 1)
> +		goto error;
> +
> +	mask_size = CPU_ALLOC_SIZE(cpus);
> +	CPU_ZERO_S(mask_size, cpu_mask);
> +	CPU_ZERO_S(mask_size, *pin_mask);
> +
> +	if (sched_getaffinity(0, mask_size, cpu_mask) == -1)
> +		goto error;
> +
> +	for (i = 0; i < cpus; i++) {
> +		if (CPU_ISSET_S(i, mask_size, cpu_mask)) {
> +			CPU_SET_S(i, mask_size, *pin_mask);
> +			break;
> +		}
> +	}
> +
> +	if (CPU_COUNT_S(mask_size, *pin_mask) < 1)
> +		goto error;
> +
> +	CPU_FREE(cpu_mask);
> +	*m_size = mask_size;
> +	return 0;
> +
> +error:
> +	if (cpu_mask)
> +		CPU_FREE(cpu_mask);
> +	if (*pin_mask)
> +		CPU_FREE(*pin_mask);
> +	*pin_mask = NULL;
> +	*m_size = 0;
> +	return -1;
> +}
> +
> +static void *tsync_host_thread(void *data)
> +{
> +	struct tracecmd_time_sync *tsync = NULL;
> +
> +	tsync = (struct tracecmd_time_sync *)data;
> +
> +	tracecmd_tsync_with_guest(tsync);
> +
> +	tracecmd_msg_handle_close(tsync->msg_handle);
> +	tsync->msg_handle = NULL;
> +
> +	pthread_exit(0);
> +}
> +
> +int tracecmd_host_tsync(struct buffer_instance *instance,
> +			 unsigned int tsync_port)
> +{
> +	struct tracecmd_msg_handle *msg_handle = NULL;
> +	cpu_set_t *pin_mask = NULL;
> +	pthread_attr_t attrib;
> +	size_t mask_size = 0;
> +	int ret;
> +	int fd;
> +
> +	if (!instance->tsync.sync_proto)
> +		return -1;
> +
> +	fd = open_vsock(instance->cid, tsync_port);
> +	if (fd < 0) {
> +		ret = -1;
> +		goto out;
> +	}
> +	msg_handle = tracecmd_msg_handle_alloc(fd, 0);
> +	if (!msg_handle) {
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	instance->tsync.msg_handle = msg_handle;
> +	if (top_instance.ftrace->clock)
> +		instance->tsync.clock_str = strdup(top_instance.ftrace->clock);
> +	ret = sem_init(&instance->tsync.sem, 0, 0);
> +	if (ret)
> +		goto out;
> +
> +	pthread_attr_init(&attrib);
> +	pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
> +	if (!get_first_cpu(&pin_mask, &mask_size))
> +		pthread_attr_setaffinity_np(&attrib, mask_size, pin_mask);
> +
> +	ret = pthread_create(&instance->tsync_thread, &attrib,
> +			     tsync_host_thread, &instance->tsync);
> +	if (!ret)
> +		instance->tsync_thread_running = true;
> +	if (pin_mask)
> +		CPU_FREE(pin_mask);
> +	pthread_attr_destroy(&attrib);
> +
> +out:
> +	if (ret) {
> +		if (msg_handle)
> +			tracecmd_msg_handle_close(msg_handle);
> +	}
> +
> +	return ret;
> +}
> +
> +static void write_guest_time_shift(struct buffer_instance *instance)
> +{
> +	struct tracecmd_output *handle;
> +	struct iovec vector[4];
> +	long long *offsets;
> +	long long *ts;
> +	char *file;
> +	int count;
> +	int ret;
> +	int fd;
> +
> +	ret = tracecmd_tsync_get_offsets(&instance->tsync, &count, &ts, &offsets);
> +	if (ret < 0 || !count || !ts || !offsets)
> +		return;
> +
> +	file = get_guest_file(TRACE_FILENAME, instance->ftrace->name);
> +	fd = open(file, O_RDWR);
> +	if (fd < 0)
> +		die("error opening %s", file);
> +	free(file);
> +	handle = tracecmd_get_output_handle_fd(fd);
> +	vector[0].iov_len = 8;
> +	vector[0].iov_base = &top_instance.trace_id;
> +	vector[1].iov_len = 4;
> +	vector[1].iov_base = &count;
> +	vector[2].iov_len = 8 * count;
> +	vector[2].iov_base = ts;
> +	vector[3].iov_len = 8 * count;
> +	vector[3].iov_base = offsets;
> +	tracecmd_add_option_v(handle, TRACECMD_OPTION_TIME_SHIFT, vector, 4);
> +	tracecmd_append_options(handle);
> +	tracecmd_output_close(handle);
> +#ifdef TSYNC_DEBUG
> +	if (count > 1)
> +		printf("Got %d timestamp synch samples for guest %s in %lld ns trace\n\r",
> +			count, instance->ftrace->name, ts[count - 1] - ts[0]);
> +#endif
> +}
> +
> +void tracecmd_host_tsync_complete(struct buffer_instance *instance)
> +{
> +	if (!instance->tsync_thread_running)
> +		return;
> +
> +	sem_post(&instance->tsync.sem);
> +	pthread_join(instance->tsync_thread, NULL);
> +	write_guest_time_shift(instance);
> +	tracecmd_tsync_free(&instance->tsync);
> +}
> +
> +static void *tsync_agent_thread(void *data)
> +{
> +	struct tracecmd_time_sync *tsync = NULL;
> +	int sd;
> +
> +	tsync = (struct tracecmd_time_sync *)data;
> +
> +	while (true) {
> +		sd = accept(tsync->msg_handle->fd, NULL, NULL);
> +		if (sd < 0) {
> +			if (errno == EINTR)
> +				continue;
> +			goto out;
> +		}
> +		break;
> +	}
> +	close(tsync->msg_handle->fd);
> +	tsync->msg_handle->fd = sd;
> +
> +	tracecmd_tsync_with_host(tsync);
> +
> +out:
> +	tracecmd_msg_handle_close(tsync->msg_handle);
> +	tracecmd_tsync_free(tsync);
> +	free(tsync);
> +	close(sd);
> +
> +	pthread_exit(0);
> +}
> +
> +unsigned int tracecmd_guest_tsync(unsigned int tsync_protos, char *clock,
> +				  unsigned int *tsync_port,
> +				  pthread_t *thr_id)
> +{
> +	struct tracecmd_time_sync *tsync = NULL;
> +	cpu_set_t *pin_mask = NULL;
> +	pthread_attr_t attrib;
> +	size_t mask_size = 0;
> +	unsigned int proto;
> +	int ret;
> +	int fd;
> +
> +	fd = -1;
> +	proto = tracecmd_tsync_proto_select(tsync_protos);
> +	if (!proto)
> +		return 0;
> +#ifdef VSOCK

Instead of this, perhaps we should not have this file get compiled if
VSOCK is not defined?

-- Steve


> +	fd = make_vsock(VMADDR_PORT_ANY);
> +	if (fd < 0)
> +		goto error;
> +
> +	ret = get_vsock_port(fd, tsync_port);
> +	if (ret < 0)
> +		goto error;
> +#else
> +	return 0;
> +#endif
> +
> +	tsync = calloc(1, sizeof(struct tracecmd_time_sync));
> +	tsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);
> +	if (clock)
> +		tsync->clock_str = strdup(clock);
> +
> +	pthread_attr_init(&attrib);
> +	tsync->sync_proto = proto;
> +	pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
> +	if (!get_first_cpu(&pin_mask, &mask_size))
> +		pthread_attr_setaffinity_np(&attrib, mask_size, pin_mask);
> +
> +	ret = pthread_create(thr_id, &attrib, tsync_agent_thread, tsync);
> +
> +	if (pin_mask)
> +		CPU_FREE(pin_mask);
> +	pthread_attr_destroy(&attrib);
> +
> +	if (ret)
> +		goto error;
> +
> +	return proto;
> +
> +error:
> +	if (tsync) {
> +		if (tsync->msg_handle)
> +			tracecmd_msg_handle_close(tsync->msg_handle);
> +		if (tsync->clock_str)
> +			free(tsync->clock_str);
> +		free(tsync);
> +	}
> +	if (fd > 0)
> +		close(fd);
> +	return 0;
> +}
> diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
> index 05ec021..9fa61e1 100644
> --- a/tracecmd/trace-usage.c
> +++ b/tracecmd/trace-usage.c
> @@ -60,6 +60,10 @@ static struct usage_help usage_help[] = {
>  		"          --no-filter include trace-cmd threads in the trace\n"
>  		"          --proc-map save the traced processes address map into the trace.dat file\n"
>  		"          --user execute the specified [command ...] as given user\n"
> +		"          --tsync-interval set the loop interval, in ms, for timestamps synchronization with guests:"
> +		"               If a negative number is specified, timestamps synchronization is disabled"
> +		"               If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
> +		"                                                         at the beginnig and at the end of the trace\n"
>  	},
>  	{
>  		"start",


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument
  2019-12-10 15:48       ` Steven Rostedt
@ 2019-12-11  8:21         ` Tzvetomir Stoyanov
  2019-12-11 15:01           ` Steven Rostedt
  0 siblings, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2019-12-11  8:21 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

On Tue, Dec 10, 2019 at 5:48 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
> On Tue, 10 Dec 2019 10:49:43 +0200
> Tzvetomir Stoyanov <tz.stoyanov@gmail.com> wrote:
>
> > > > +     if (guest_config) {
> > > > +             /* If -C is specified, prepend clock to all guest VM flags */
> > > > +             for_all_instances(instance) {
> > > > +                     if (top_instance.ftrace->clock) {
> > > > +                             if (is_guest(instance)) {
> > >
> > > We should only append this, if the guest didn't have a clock set
> > > already. As the change log seems to say, if the user states a "-C
> > > clock" for the guest, that should take precedence over the host clock
> > > set. That is, a user may specifically state that they are using a
> > > different clock. If we have frequency and offset set, it should still
> > > work with different clocks.
> >
> > The guest config string is not parsed in the host context, that's why the host
> > doesn't know if a guest has an explicit  "-C clock" argument.
> > I can parse the guest config here, but this will complicate the implementation.
> > Using the current approach still guarantees that the user specified
> > config has higher
> > priority than injected one - add_argv() API prepends to the beginning
> > of the string, so
> > user arguments are always after the injected one. When guest parses
> > the string, in case of
> > duplicated "-C clock" arguments, the last one wins.
>
> I'm confused. I'm looking at this:
>
>
>         for (;;) {
>                 [..]
>                 switch (c) {
>                 [..]
>                 case 'A':
>                         [..]
>                         ctx->instance->flags |= BUFFER_FL_GUEST;
>                 [..]
>                 case 'C':
>                         ctx->instance->ftrace->clock = optarg;
>                         guest_config = true;
>                         break;
>                 [..]
>                 }
>                 [..]
>         }
>         [..]
>         if (guest_config) {
>                 /* If -C is specified, prepend clock to all guest VM flags */
>                 for_all_instances(instance) {
>                         if (top_instance.ftrace->clock) {
>
> Why can't we have here:
>
>  if (top_instance.ftrace->clock && !instance->ftrace->clock)
>
> If the guest instance was given a -C, I would think we don't want to add
> another -C to pass to that guest?
>

In case of a guest ("-A" option), the logic skips the switch(), so the
guest args are not parsed.
There is a check, right before the switch() :

        /*
         * If the current instance is to record a guest, then save
         * all the arguments for this instance.
         */
        if (c != 'B' && c != 'A' && is_guest(ctx->instance)) {
            add_arg(ctx->instance, c, opts, long_options, optarg);
            continue;
        }

I can put inside that if() a check for "-C" guest argument, but it
will look like a hack.

The confusion is that guest_config is set to true for any "-C" host argument,
including those for instances, but only the one from the top instance
is used to inject
guest clock arg.


> -- Steve
>
>                                 if (is_guest(instance)) {
>                                         add_argv(instance,
>                                                  (char *)top_instance.ftrace->clock,
>                                                  true);
>                                         add_argv(instance, "-C", true);
>                                 }
>                         }
>                 }
>         }



-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument
  2019-12-11  8:21         ` Tzvetomir Stoyanov
@ 2019-12-11 15:01           ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-11 15:01 UTC (permalink / raw)
  To: Tzvetomir Stoyanov; +Cc: linux-trace-devel

On Wed, 11 Dec 2019 10:21:44 +0200
Tzvetomir Stoyanov <tz.stoyanov@gmail.com> wrote:

> In case of a guest ("-A" option), the logic skips the switch(), so the
> guest args are not parsed.
> There is a check, right before the switch() :

Ah, thanks, I forgot about that.


> 
>         /*
>          * If the current instance is to record a guest, then save
>          * all the arguments for this instance.
>          */
>         if (c != 'B' && c != 'A' && is_guest(ctx->instance)) {
>             add_arg(ctx->instance, c, opts, long_options, optarg);
>             continue;
>         }
> 
> I can put inside that if() a check for "-C" guest argument, but it
> will look like a hack.

I disagree. It's no more of a "hack" than appending a "-C" to the
arguments. I think it's the right solution. We can simply add:

	if (c == 'C')
		ctx->instance->flags |= BUFFER_FL_HAS_CLOCK;

Then we could test if that flag is set for the instance below.

-- Steve

> 
> The confusion is that guest_config is set to true for any "-C" host argument,
> including those for instances, but only the one from the top instance
> is used to inject
> guest clock arg.
> 
> 
> > -- Steve
> >
> >                                 if (is_guest(instance)) {
> >                                         add_argv(instance,
> >                                                  (char *)top_instance.ftrace->clock,
> >                                                  true);
> >                                         add_argv(instance, "-C", true);
> >                                 }
> >                         }
> >                 }
> >         }  
> 
> 
> 


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization
  2019-12-10 18:39   ` Steven Rostedt
@ 2019-12-12 12:34     ` Tzvetomir Stoyanov
  2019-12-12 14:54       ` Steven Rostedt
  2019-12-12 14:00     ` Tzvetomir Stoyanov
  1 sibling, 1 reply; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2019-12-12 12:34 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

 Thanks, Steven!
I will prepare the next version addressing the comments.

On Tue, Dec 10, 2019 at 8:39 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
[ ... ]
> >
> > +static int tracecmd_msg_send_nofree(int fd, struct tracecmd_msg *msg)
>
> We probably shouldn't have a static function that starts with
> "tracecmd_".

I changed this one, but noticed few other static "tracecmd_" functions
in the legacy code, will rename them in a separate patch.

>
[ ... ]
> >  static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
> > -                       bool use_fifos, unsigned long long trace_id)
> > +                       bool use_fifos, unsigned long long trace_id,
> > +                       unsigned int tsync_protos)
> >  {
> >       size_t args_size = 0;
> >       char *p;
> > @@ -824,7 +848,11 @@ static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
> >               args_size += strlen(argv[i]) + 1;
> >
> >       msg->hdr.size = htonl(ntohl(msg->hdr.size) + args_size);
> > -     msg->trace_req.flags = use_fifos ? htonl(MSG_TRACE_USE_FIFOS) : htonl(0);
> > +     msg->trace_req.flags = 0;
> > +     if (use_fifos)
> > +             msg->trace_req.flags |= MSG_TRACE_USE_FIFOS;
>
> Why the change to flags here?

Fixed, It was a leftover from a previous version of this patch, where
flags were extended with time sync specific one.
However, I removed that time sync specific flag but forgot about this change.

>
[ ... ]
> > +
> > +/**
> > + * tracecmd_tsync_proto_select - Select time sync protocol, to be used for
> > + *           timestamp synchronization with a peer.
> > + *
> > + * @protos: Bitmask of time sync protocols, supported by the peer.
>
> Hmm, so we are limiting the number of protocols that we will ever
> support to 32? This makes me nervous (I always think of the famous
> quote of "Who will ever need more than 64K of RAM?")
>
> To be safe, we should probably make this an array:
>
>         int tracecmd_tsync_proto_select(unsigned int *protos, int words)
>
> The proto_id should be a bit, not a mask, that is:
>
>         int word;
>
>         for (word = 0; word < words; word++) {
>
>                 for (proto = tsync_proto_list; proto; proto = proto->next) {
>                         if (proto->proto_id < word * 32)
>                                 continue;
>
>                         id = proto->proto_id - word * 32;
>                         if (id >= 32)
>                                 continue;
>
>                         if ((1 << id) & protos[word]) {
>                                 if (selected)
>                                         [...]
>
> While we only have a single word, callers would just do:
>
>         unsigned int protos;
>
>         ret = tracecmd_tsync_proto_select(&protos, 1);
>
> At least this will never limit us to just 32 protocols, even though we
> will likely never need more. But we are safe if we ever do ;-)
>

I changed this and tracecmd_tsync_proto_getall() to work with
unlimited bitmasks,
but in order to support more than 32 protocols the trace request  packet must be
changed. In the current patch I added field
                                 be32 tsync_protos;
in struct tracecmd_msg_trace_req, which is bitmask for all time sync
protocols, supported
by the host. Defining this as a fixed size bitmap simplifies a lot the
protocol. We can add a
dynamic bitmask as part of the trace request  packet payload, but that
payload is already used
for the trace arguments. We can extend the payload to hold both
dynamic bitmask and trace arguments,
but I think it will complicate a lot the packet handling and the
benefit will be only the posibility to support
more than 32 time sync protocols.

> > + *
[ ... ]

-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization
  2019-12-10 18:39   ` Steven Rostedt
  2019-12-12 12:34     ` Tzvetomir Stoyanov
@ 2019-12-12 14:00     ` Tzvetomir Stoyanov
  1 sibling, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2019-12-12 14:00 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

Forgot to comment on that in the previous reply :

On Tue, Dec 10, 2019 at 8:39 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
[ ... ]
> > +
> > +unsigned int tracecmd_guest_tsync(unsigned int tsync_protos, char *clock,
> > +                               unsigned int *tsync_port,
> > +                               pthread_t *thr_id)
> > +{
> > +     struct tracecmd_time_sync *tsync = NULL;
> > +     cpu_set_t *pin_mask = NULL;
> > +     pthread_attr_t attrib;
> > +     size_t mask_size = 0;
> > +     unsigned int proto;
> > +     int ret;
> > +     int fd;
> > +
> > +     fd = -1;
> > +     proto = tracecmd_tsync_proto_select(tsync_protos);
> > +     if (!proto)
> > +             return 0;
> > +#ifdef VSOCK
>
> Instead of this, perhaps we should not have this file get compiled if
> VSOCK is not defined?

I would recommend to isolate VSOCK defines inside the time synchronization code.
The caller might not be aware of time synchronization internal dependencies.
The current implementation relies on vsoks, but we may change this in
the future and
this should not affect the callers.

>
> -- Steve
>
>
> > +     fd = make_vsock(VMADDR_PORT_ANY);
> > +     if (fd < 0)
> > +             goto error;
> > +
> > +     ret = get_vsock_port(fd, tsync_port);
> > +     if (ret < 0)
> > +             goto error;
> > +#else
> > +     return 0;
> > +#endif
> > +
> > +     tsync = calloc(1, sizeof(struct tracecmd_time_sync));
> > +     tsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);
> > +     if (clock)
> > +             tsync->clock_str = strdup(clock);
> > +
> > +     pthread_attr_init(&attrib);
> > +     tsync->sync_proto = proto;
> > +     pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
> > +     if (!get_first_cpu(&pin_mask, &mask_size))
> > +             pthread_attr_setaffinity_np(&attrib, mask_size, pin_mask);
> > +
> > +     ret = pthread_create(thr_id, &attrib, tsync_agent_thread, tsync);
> > +
> > +     if (pin_mask)
> > +             CPU_FREE(pin_mask);
> > +     pthread_attr_destroy(&attrib);
> > +
> > +     if (ret)
> > +             goto error;
> > +
> > +     return proto;
> > +
> > +error:
> > +     if (tsync) {
> > +             if (tsync->msg_handle)
> > +                     tracecmd_msg_handle_close(tsync->msg_handle);
> > +             if (tsync->clock_str)
> > +                     free(tsync->clock_str);
> > +             free(tsync);
> > +     }
> > +     if (fd > 0)
> > +             close(fd);
> > +     return 0;
> > +}
> > diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
> > index 05ec021..9fa61e1 100644
> > --- a/tracecmd/trace-usage.c
> > +++ b/tracecmd/trace-usage.c
> > @@ -60,6 +60,10 @@ static struct usage_help usage_help[] = {
> >               "          --no-filter include trace-cmd threads in the trace\n"
> >               "          --proc-map save the traced processes address map into the trace.dat file\n"
> >               "          --user execute the specified [command ...] as given user\n"
> > +             "          --tsync-interval set the loop interval, in ms, for timestamps synchronization with guests:"
> > +             "               If a negative number is specified, timestamps synchronization is disabled"
> > +             "               If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
> > +             "                                                         at the beginnig and at the end of the trace\n"
> >       },
> >       {
> >               "start",
>


-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization
  2019-12-12 12:34     ` Tzvetomir Stoyanov
@ 2019-12-12 14:54       ` Steven Rostedt
  0 siblings, 0 replies; 42+ messages in thread
From: Steven Rostedt @ 2019-12-12 14:54 UTC (permalink / raw)
  To: Tzvetomir Stoyanov; +Cc: linux-trace-devel

On Thu, 12 Dec 2019 14:34:39 +0200
Tzvetomir Stoyanov <tz.stoyanov@gmail.com> wrote:

>  Thanks, Steven!
> I will prepare the next version addressing the comments.
> 
> On Tue, Dec 10, 2019 at 8:39 PM Steven Rostedt <rostedt@goodmis.org> wrote:
> >  
> [ ... ]
> > >
> > > +static int tracecmd_msg_send_nofree(int fd, struct tracecmd_msg *msg)  
> >
> > We probably shouldn't have a static function that starts with
> > "tracecmd_".  
> 
> I changed this one, but noticed few other static "tracecmd_" functions
> in the legacy code, will rename them in a separate patch.

Yeah, those should probably be changed too.

> >  
> [ ... ]
> > > +
> > > +/**
> > > + * tracecmd_tsync_proto_select - Select time sync protocol, to be used for
> > > + *           timestamp synchronization with a peer.
> > > + *
> > > + * @protos: Bitmask of time sync protocols, supported by the peer.  
> >
> > Hmm, so we are limiting the number of protocols that we will ever
> > support to 32? This makes me nervous (I always think of the famous
> > quote of "Who will ever need more than 64K of RAM?")
> >
> > To be safe, we should probably make this an array:
> >
> >         int tracecmd_tsync_proto_select(unsigned int *protos, int words)
> >
> > The proto_id should be a bit, not a mask, that is:
> >
> >         int word;
> >
> >         for (word = 0; word < words; word++) {
> >
> >                 for (proto = tsync_proto_list; proto; proto = proto->next) {
> >                         if (proto->proto_id < word * 32)
> >                                 continue;
> >
> >                         id = proto->proto_id - word * 32;
> >                         if (id >= 32)
> >                                 continue;
> >
> >                         if ((1 << id) & protos[word]) {
> >                                 if (selected)
> >                                         [...]
> >
> > While we only have a single word, callers would just do:
> >
> >         unsigned int protos;
> >
> >         ret = tracecmd_tsync_proto_select(&protos, 1);
> >
> > At least this will never limit us to just 32 protocols, even though we
> > will likely never need more. But we are safe if we ever do ;-)
> >  
> 
> I changed this and tracecmd_tsync_proto_getall() to work with
> unlimited bitmasks,
> but in order to support more than 32 protocols the trace request  packet must be
> changed. In the current patch I added field
>                                  be32 tsync_protos;
> in struct tracecmd_msg_trace_req, which is bitmask for all time sync
> protocols, supported
> by the host. Defining this as a fixed size bitmap simplifies a lot the
> protocol. We can add a
> dynamic bitmask as part of the trace request  packet payload, but that
> payload is already used
> for the trace arguments. We can extend the payload to hold both
> dynamic bitmask and trace arguments,
> but I think it will complicate a lot the packet handling and the
> benefit will be only the posibility to support
> more than 32 time sync protocols.

Perhaps instead, we can add an option to which 32bit word the protocols
are used. That is, we pass an index of what the tsync_protos are, and
the response can pick one of them, and even say how many more words it
has.

That is, I don't think we should change the protocol to pass the entire
bitmask. Just part of it, and if we find a match, we use that. That
should keep it from being too complicated, right?

-- Steve


> 
> > > + *  
> [ ... ]
> 


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll()
  2019-12-03 10:35 ` [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll() Tzvetomir Stoyanov (VMware)
@ 2019-12-20 13:50   ` Steven Rostedt
  2020-01-06 14:30     ` Tzvetomir Stoyanov
  0 siblings, 1 reply; 42+ messages in thread
From: Steven Rostedt @ 2019-12-20 13:50 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Tue,  3 Dec 2019 12:35:07 +0200
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> From: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> 
> Implementations of htonll() and ntohll() are added as
> macros, if they are not already defined.

I'm somewhat nervous about this being in a global header, as it is
mostly generic. I wonder if we should prefix these with "tracecmd_"
just to avoid any name collisions.

We could update this later when we make trace-cmd into a library.

-- Steve


> 
> Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> ---
>  include/trace-cmd/trace-msg.h | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/include/trace-cmd/trace-msg.h b/include/trace-cmd/trace-msg.h
> index aab8a69..654ca43 100644
> --- a/include/trace-cmd/trace-msg.h
> +++ b/include/trace-cmd/trace-msg.h
> @@ -12,4 +12,14 @@
>  
>  extern unsigned int page_size;
>  
> +#ifndef htonll
> +# if __BYTE_ORDER == __LITTLE_ENDIAN
> +#define htonll(x) __bswap_64(x)
> +#define ntohll(x) __bswap_64(x)
> +#else
> +#define htonll(x) (x)
> +#define ntohll(x) (x)
> +#endif
> +#endif
> +
>  #endif /* _TRACE_MSG_H_ */


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll()
  2019-12-20 13:50   ` Steven Rostedt
@ 2020-01-06 14:30     ` Tzvetomir Stoyanov
  0 siblings, 0 replies; 42+ messages in thread
From: Tzvetomir Stoyanov @ 2020-01-06 14:30 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel

On Fri, Dec 20, 2019 at 3:50 PM Steven Rostedt <rostedt@goodmis.org> wrote:
>
> On Tue,  3 Dec 2019 12:35:07 +0200
> "Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:
>
> > From: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> >
> > Implementations of htonll() and ntohll() are added as
> > macros, if they are not already defined.
>
> I'm somewhat nervous about this being in a global header, as it is
> mostly generic. I wonder if we should prefix these with "tracecmd_"
> just to avoid any name collisions.
>
> We could update this later when we make trace-cmd into a library.
>

I can move them to trace-msg.c file, as these are used only there.
Note that a lot of clean-ups are required for libtracecmd.

> -- Steve
>
>
> >
> > Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> > ---
> >  include/trace-cmd/trace-msg.h | 10 ++++++++++
> >  1 file changed, 10 insertions(+)
> >
> > diff --git a/include/trace-cmd/trace-msg.h b/include/trace-cmd/trace-msg.h
> > index aab8a69..654ca43 100644
> > --- a/include/trace-cmd/trace-msg.h
> > +++ b/include/trace-cmd/trace-msg.h
> > @@ -12,4 +12,14 @@
> >
> >  extern unsigned int page_size;
> >
> > +#ifndef htonll
> > +# if __BYTE_ORDER == __LITTLE_ENDIAN
> > +#define htonll(x) __bswap_64(x)
> > +#define ntohll(x) __bswap_64(x)
> > +#else
> > +#define htonll(x) (x)
> > +#define ntohll(x) (x)
> > +#endif
> > +#endif
> > +
> >  #endif /* _TRACE_MSG_H_ */
>


-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2020-01-06 14:30 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-03 10:35 [PATCH v17 00/18]Timestamp synchronization of host - guest tracing session Tzvetomir Stoyanov (VMware)
2019-12-03 10:35 ` [PATCH v17 01/18] trace-cmd: Implement new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov (VMware)
2019-12-03 10:35 ` [PATCH v17 02/18] trace-cmd: Add support for negative time offsets in trace.dat file Tzvetomir Stoyanov (VMware)
2019-12-03 10:35 ` [PATCH v17 03/18] trace-cmd: Add implementations of htonll() and ntohll() Tzvetomir Stoyanov (VMware)
2019-12-20 13:50   ` Steven Rostedt
2020-01-06 14:30     ` Tzvetomir Stoyanov
2019-12-03 10:35 ` [PATCH v17 04/18] trace-cmd: Add new library APIs for ftrace instances Tzvetomir Stoyanov (VMware)
2019-12-04 16:17   ` Steven Rostedt
2019-12-05 14:40     ` Tzvetomir Stoyanov
2019-12-03 10:35 ` [PATCH v17 05/18] trace-cmd: Add new library API for local CPU count Tzvetomir Stoyanov (VMware)
2019-12-04 20:09   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 06/18] trace-cmd: Add new library API for reading ftrace buffers Tzvetomir Stoyanov (VMware)
2019-12-04 21:10   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 07/18] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov (VMware)
2019-12-04 21:35   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 08/18] trace-cmd: Implement new API tracecmd_add_option_v() Tzvetomir Stoyanov (VMware)
2019-12-04 21:47   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 09/18] trace-cmd: Add new API to generate a unique ID of the tracing session Tzvetomir Stoyanov (VMware)
2019-12-03 10:35 ` [PATCH v17 10/18] trace-cmd: Store the session tracing ID in the trace.dat file Tzvetomir Stoyanov (VMware)
2019-12-03 10:35 ` [PATCH v17 11/18] trace-cmd: Exchange tracing IDs between host and guest Tzvetomir Stoyanov (VMware)
2019-12-04 22:03   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 12/18] trace-cmd: Implement new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov (VMware)
2019-12-05  0:46   ` Steven Rostedt
2019-12-05 15:09     ` Tzvetomir Stoyanov
2019-12-03 10:35 ` [PATCH v17 13/18] trace-cmd: Add guest information in host's trace.dat file Tzvetomir Stoyanov (VMware)
2019-12-05  0:59   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 14/18] trace-cmd: Add host trace clock as guest trace argument Tzvetomir Stoyanov (VMware)
2019-12-09 19:31   ` Steven Rostedt
2019-12-10  8:49     ` Tzvetomir Stoyanov
2019-12-10 15:48       ` Steven Rostedt
2019-12-11  8:21         ` Tzvetomir Stoyanov
2019-12-11 15:01           ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 15/18] trace-cmd: Refactor few trace-cmd internal functions Tzvetomir Stoyanov (VMware)
2019-12-09 19:32   ` Steven Rostedt
2019-12-03 10:35 ` [PATCH v17 16/18] trace-cmd: Basic infrastructure for host - guest timestamp synchronization Tzvetomir Stoyanov (VMware)
2019-12-10 17:04   ` Steven Rostedt
2019-12-10 18:39   ` Steven Rostedt
2019-12-12 12:34     ` Tzvetomir Stoyanov
2019-12-12 14:54       ` Steven Rostedt
2019-12-12 14:00     ` Tzvetomir Stoyanov
2019-12-03 10:35 ` [PATCH v17 17/18] trace-cmd: [POC] PTP-like algorithm " Tzvetomir Stoyanov (VMware)
2019-12-03 10:35 ` [PATCH v17 18/18] trace-cmd: Debug scripts for " Tzvetomir Stoyanov (VMware)

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).