All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events.
@ 2019-03-08 13:44 Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 1/9] trace-cmd: Implemented new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

[
 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.
]


This patch series is a POC for timestamps sync between guest and host machines.
The algorithm relies on matching time stamps of guest and host vsock events.

The patch series depends on Slavomir's changes, introduced by the v8 patch series
"Add VM kernel tracing over vsockets and FIFOs"

Tzvetomir Stoyanov (9):
  trace-cmd: Implemented new lib API: tracecmd_local_events_system()
  trace-cmd: Added support for negative time offsets in trace.dat file
  trace-cmd: Fix tracecmd_read_page_record() to read more than one event
  trace-cmd: Added implementation of htonll() and ntohll()
  trace-cmd: Refactored few functions in trace-record.c
  trace-cmd: Find and store pids of tasks, which run virtual CPUs of
    given VM
  trace-cmd: Implemented new API tracecmd_add_option_v()
  trace-cmd: Implemented new option in trace.dat file:
    TRACECMD_OPTION_TIME_SHIFT
  trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm,
    using vsock events.

 include/trace-cmd/trace-cmd.h    |  19 +-
 include/traceevent/event-parse.h |   1 +
 lib/trace-cmd/trace-input.c      | 137 ++++++-
 lib/trace-cmd/trace-util.c       |  99 +++--
 tracecmd/Makefile                |   1 +
 tracecmd/include/trace-local.h   |  60 +++
 tracecmd/include/trace-msg.h     |  10 +
 tracecmd/trace-msg.c             | 284 +++++++++++++-
 tracecmd/trace-output.c          | 117 +++++-
 tracecmd/trace-read.c            |   4 +-
 tracecmd/trace-record.c          | 227 +++++++++--
 tracecmd/trace-timesync.c        | 622 +++++++++++++++++++++++++++++++
 12 files changed, 1467 insertions(+), 114 deletions(-)
 create mode 100644 tracecmd/trace-timesync.c

-- 
2.20.1


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

* [PATCH v5 1/9] trace-cmd: Implemented new lib API: tracecmd_local_events_system()
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 2/9] trace-cmd: Added support for negative time offsets in trace.dat file Tzvetomir Stoyanov
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

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 |  2 +
 lib/trace-cmd/trace-util.c    | 99 +++++++++++++++++++++++------------
 2 files changed, 68 insertions(+), 33 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 27ba89d..0067882 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -32,6 +32,8 @@ void tracecmd_unload_plugins(struct tep_plugin_list *list, struct tep_handle *pe
 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,
+						char **sys_names);
 int tracecmd_fill_local_events(const char *tracing_dir, struct tep_handle *pevent);
 char **tracecmd_local_plugins(const char *tracing_dir);
 
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index b5aea39..0e925ed 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -1119,43 +1119,15 @@ 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)
-{
-	struct tep_handle *pevent = NULL;
-
-	pevent = tep_alloc();
-	if (!pevent)
-		return NULL;
-
-	if (tracecmd_fill_local_events(tracing_dir, pevent)) {
-		tep_free(pevent);
-		pevent = NULL;
-	}
-
-	return pevent;
-}
-
-/**
- * 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
- *
- * Returns whether the operation succeeded
- */
-int tracecmd_fill_local_events(const char *tracing_dir, struct tep_handle *pevent)
+static int tracecmd_fill_local_events_system(const char *tracing_dir,
+					     struct tep_handle *pevent,
+					     char **sys_names)
 {
+	int ret, i, failure = 0;
 	struct dirent *dent;
 	char *events_dir;
 	struct stat st;
 	DIR *dir;
-	int ret, failure = 0;
 
 	if (!tracing_dir)
 		return -1;
@@ -1189,7 +1161,16 @@ int tracecmd_fill_local_events(const char *tracing_dir, struct tep_handle *peven
 		if (strcmp(name, ".") == 0 ||
 		    strcmp(name, "..") == 0)
 			continue;
-
+		if (sys_names) {
+			i = 0;
+			while (sys_names[i]) {
+				if (strcmp(name, sys_names[i]) == 0)
+					break;
+				i++;
+			}
+			if (sys_names[i] == NULL)
+				continue;
+		}
 		sys = append_file(events_dir, name);
 		ret = stat(sys, &st);
 		if (ret < 0 || !S_ISDIR(st.st_mode)) {
@@ -1217,6 +1198,58 @@ int tracecmd_fill_local_events(const char *tracing_dir, struct tep_handle *peven
 	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,
+						char **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)) {
+		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.
+ * @pevent: Allocated pevent which will be filled
+ *
+ * Returns whether the operation succeeded
+ */
+int tracecmd_fill_local_events(const char *tracing_dir,
+			       struct tep_handle *pevent)
+{
+	return tracecmd_fill_local_events_system(tracing_dir, pevent, NULL);
+}
+
 /**
  * tracecmd_local_plugins - returns an array of available tracer plugins
  * @tracing_dir: The directory that contains the tracing directory
-- 
2.20.1


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

* [PATCH v5 2/9] trace-cmd: Added support for negative time offsets in trace.dat file
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 1/9] trace-cmd: Implemented new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 3/9] trace-cmd: Fix tracecmd_read_page_record() to read more than one event Tzvetomir Stoyanov
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

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 0067882..3f0ab4f 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -126,7 +126,7 @@ int tracecmd_is_buffer_instance(struct tracecmd_input *handle);
 void tracecmd_create_top_instance(char *name);
 void tracecmd_remove_instances(void);
 
-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 34a8301..be6720c 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -91,7 +91,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;
@@ -2082,7 +2082,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;
 }
@@ -2099,7 +2099,7 @@ void tracecmd_set_ts2secs(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 804022a..e309672 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;
@@ -1413,7 +1413,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.20.1


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

* [PATCH v5 3/9] trace-cmd: Fix tracecmd_read_page_record() to read more than one event
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 1/9] trace-cmd: Implemented new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 2/9] trace-cmd: Added support for negative time offsets in trace.dat file Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 4/9] trace-cmd: Added implementation of htonll() and ntohll() Tzvetomir Stoyanov
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

The kbuffer_next_event() will return the next event on the sub buffer.
If we pass in the last_record to tracecmd_read_page_record(), it
initializes the sub buffer, and by calling kbuffer_next_event()
(instead of kbuffer_read_event()), the second event on the sub buffer
is returned. This causes the match of the last_record not to match if
the last_record happens to be the first event on the sub buffer.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 lib/trace-cmd/trace-input.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index be6720c..0a6e820 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -1679,18 +1679,22 @@ tracecmd_read_page_record(struct tep_handle *pevent, void *page, int size,
 			goto out_free;
 		}
 
-		do {
+		ptr = kbuffer_read_event(kbuf, &ts);
+		while (ptr < last_record->data) {
 			ptr = kbuffer_next_event(kbuf, NULL);
 			if (!ptr)
 				break;
-		} while (ptr < last_record->data);
+			if (ptr == last_record->data)
+				break;
+		}
 		if (ptr != last_record->data) {
 			warning("tracecmd_read_page_record: could not find last_record");
 			goto out_free;
 		}
-	}
+		ptr = kbuffer_next_event(kbuf, &ts);
+	} else
+		ptr = kbuffer_read_event(kbuf, &ts);
 
-	ptr = kbuffer_read_event(kbuf, &ts);
 	if (!ptr)
 		goto out_free;
 
-- 
2.20.1


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

* [PATCH v5 4/9] trace-cmd: Added implementation of htonll() and ntohll()
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
                   ` (2 preceding siblings ...)
  2019-03-08 13:44 ` [PATCH v5 3/9] trace-cmd: Fix tracecmd_read_page_record() to read more than one event Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 5/9] trace-cmd: Refactored few functions in trace-record.c Tzvetomir Stoyanov
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

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

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

diff --git a/tracecmd/include/trace-msg.h b/tracecmd/include/trace-msg.h
index b7fe10b..445f799 100644
--- a/tracecmd/include/trace-msg.h
+++ b/tracecmd/include/trace-msg.h
@@ -15,4 +15,14 @@ extern unsigned int page_size;
 void plog(const char *fmt, ...);
 void pdie(const char *fmt, ...);
 
+#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.20.1


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

* [PATCH v5 5/9] trace-cmd: Refactored few functions in trace-record.c
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
                   ` (3 preceding siblings ...)
  2019-03-08 13:44 ` [PATCH v5 4/9] trace-cmd: Added implementation of htonll() and ntohll() Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 6/9] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

In order to reuse the code inside the trace-cmd application, few
functions from trace-record.c are refactored:
  - make_instances() and tracecmd_remove_instances() are splited.
New ones are created: tracecmd_make_instance() and tracecmd_remove_instance(),
which are visible outside the trace-record.c
  - Following functions are made non-static: tracecmd_init_instance()
get_instance_dir(), write_instance_file(), write_tracing_on(),
tracecmd_set_clock()
  - New function is implemented: tracecmd_local_cpu_count(), an internal
API to get local_cpu_count.

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

diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index d7bdb1f..8413054 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -235,6 +235,15 @@ 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);
+void write_tracing_on(struct buffer_instance *instance, int on);
+char *get_instance_dir(struct buffer_instance *instance);
+int write_instance_file(struct buffer_instance *instance,
+			const char *file, const char *str, const char *type);
+void tracecmd_init_instance(struct buffer_instance *instance);
+void tracecmd_make_instance(struct buffer_instance *instance);
+int tracecmd_local_cpu_count(void);
+void tracecmd_set_clock(struct buffer_instance *instance);
+void tracecmd_remove_instance(struct buffer_instance *instance);
 
 /* No longer in event-utils.h */
 void __noreturn die(const char *fmt, ...); /* Can be overriden */
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 177060d..3950242 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -184,7 +184,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;
 }
@@ -308,7 +308,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;
@@ -495,7 +495,7 @@ 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)
@@ -791,8 +791,7 @@ get_instance_file(struct buffer_instance *instance, const char *file)
 	return path;
 }
 
-static char *
-get_instance_dir(struct buffer_instance *instance)
+char *get_instance_dir(struct buffer_instance *instance)
 {
 	char *buf;
 	char *path;
@@ -1668,9 +1667,8 @@ static int write_file(const char *file, const char *str, const char *type)
 	return ret;
 }
 
-static int
-write_instance_file(struct buffer_instance *instance,
-		    const char *file, const char *str, const char *type)
+int write_instance_file(struct buffer_instance *instance,
+			const char *file, const char *str, const char *type)
 {
 	char *path;
 	int ret;
@@ -1946,7 +1944,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;
@@ -2262,7 +2260,7 @@ void tracecmd_enable_events(void)
 	enable_events(first_instance);
 }
 
-static void set_clock(struct buffer_instance *instance)
+void tracecmd_set_clock(struct buffer_instance *instance)
 {
 	char *path;
 	char *content;
@@ -4316,49 +4314,57 @@ static void clear_func_filters(void)
 	}
 }
 
-static void make_instances(void)
+void tracecmd_make_instance(struct buffer_instance *instance)
 {
-	struct buffer_instance *instance;
 	struct stat st;
 	char *path;
 	int ret;
 
+	path = get_instance_dir(instance);
+	ret = stat(path, &st);
+	if (ret < 0) {
+		ret = mkdir(path, 0777);
+		if (ret < 0)
+			die("mkdir %s", path);
+	} else
+		/* Don't delete instances that already exist */
+		instance->flags |= BUFFER_FL_KEEP;
+	tracecmd_put_tracing_file(path);
+
+}
+
+static void make_instances(void)
+{
+	struct buffer_instance *instance;
+
 	for_each_instance(instance) {
 		if (is_guest(instance))
 			continue;
+		tracecmd_make_instance(instance);
+	}
+}
 
-		path = get_instance_dir(instance);
-		ret = stat(path, &st);
-		if (ret < 0) {
-			ret = mkdir(path, 0777);
-			if (ret < 0)
-				die("mkdir %s", path);
-		} else
-			/* Don't delete instances that already exist */
-			instance->flags |= BUFFER_FL_KEEP;
-		tracecmd_put_tracing_file(path);
+void tracecmd_remove_instance(struct buffer_instance *instance)
+{
+	char *path;
+
+	if (instance->tracing_on_fd > 0) {
+		close(instance->tracing_on_fd);
+		instance->tracing_on_fd = 0;
 	}
+	path = get_instance_dir(instance);
+	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 */
 		if (is_guest(instance) || (instance->flags & BUFFER_FL_KEEP))
 			continue;
-		if (instance->tracing_on_fd > 0) {
-			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);
 	}
 }
 
@@ -4853,7 +4859,7 @@ void trace_stop(int argc, char **argv)
 	int topt = 0;
 	struct buffer_instance *instance = &top_instance;
 
-	init_instance(instance);
+	tracecmd_init_instance(instance);
 
 	for (;;) {
 		int c;
@@ -4894,7 +4900,7 @@ void trace_restart(int argc, char **argv)
 	int topt = 0;
 	struct buffer_instance *instance = &top_instance;
 
-	init_instance(instance);
+	tracecmd_init_instance(instance);
 
 	for (;;) {
 		int c;
@@ -4936,7 +4942,7 @@ void trace_reset(int argc, char **argv)
 	int topt = 0;
 	struct buffer_instance *instance = &top_instance;
 
-	init_instance(instance);
+	tracecmd_init_instance(instance);
 
 	/* if last arg is -a, then -b and -d apply to all instances */
 	int last_specified_all = 0;
@@ -5014,11 +5020,16 @@ 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);
+	tracecmd_init_instance(ctx->instance);
 	local_cpu_count = count_cpus();
 	ctx->instance->cpu_count = local_cpu_count;
 }
 
+int tracecmd_local_cpu_count(void)
+{
+	return local_cpu_count;
+}
+
 #define IS_EXTRACT(ctx) ((ctx)->curr_cmd == CMD_extract)
 #define IS_START(ctx) ((ctx)->curr_cmd == CMD_start)
 #define IS_STREAM(ctx) ((ctx)->curr_cmd == CMD_stream)
@@ -5589,7 +5600,7 @@ static void record_trace(int argc, char **argv,
 	tracecmd_disable_all_tracing(1);
 
 	for_all_instances(instance)
-		set_clock(instance);
+		tracecmd_set_clock(instance);
 
 	/* Record records the date first */
 	if (ctx->date &&
-- 
2.20.1


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

* [PATCH v5 6/9] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
                   ` (4 preceding siblings ...)
  2019-03-08 13:44 ` [PATCH v5 5/9] trace-cmd: Refactored few functions in trace-record.c Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 7/9] trace-cmd: Implemented new API tracecmd_add_option_v() Tzvetomir Stoyanov
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

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 |  1 +
 tracecmd/trace-record.c        | 68 ++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index 8413054..62f5e47 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -245,6 +245,7 @@ int tracecmd_local_cpu_count(void);
 void tracecmd_set_clock(struct buffer_instance *instance);
 void tracecmd_remove_instance(struct buffer_instance *instance);
 
+int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu);
 /* 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-record.c b/tracecmd/trace-record.c
index 3950242..d9e9c69 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -2746,10 +2746,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;
@@ -2767,6 +2769,57 @@ 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_t;
+	char path[PATH_MAX];
+	unsigned int vcpu;
+	char *buf = NULL;
+	char *cpu_str;
+	DIR *dir;
+	FILE *ft;
+	size_t n;
+	int j;
+
+
+	snprintf(path, sizeof(path), "/proc/%s/task", guest_task);
+	dir = opendir(path);
+	if (!dir)
+		return;
+
+	for (entry_t = readdir(dir); entry_t; entry_t = readdir(dir)) {
+		if (!(entry_t->d_type == DT_DIR &&
+		    is_digits(entry_t->d_name)))
+			continue;
+		snprintf(path, sizeof(path),
+			 "/proc/%s/task/%s/comm",
+			 guest_task, entry_t->d_name);
+		ft = fopen(path, "r");
+		if (!ft)
+			continue;
+		getline(&buf, &n, ft);
+		if (buf && strncmp(buf, "CPU ", 4) == 0) {
+			cpu_str = buf;
+			while (*cpu_str != '\0' &&
+				isdigit(*cpu_str) == 0)
+				cpu_str++;
+			if (*cpu_str != '\0') {
+				j = 0;
+				while (cpu_str[j] != '\0' &&
+				       isdigit(cpu_str[j]) != 0)
+					j++;
+				cpu_str[j] = '\0';
+				vcpu = atoi(cpu_str);
+				if (vcpu < VCPUS_MAX)
+					guest->cpu_pid[vcpu] = atoi(entry_t->d_name);
+			}
+		}
+		free(buf);
+		fclose(ft);
+		buf = NULL;
+	}
+}
+
 static void read_qemu_guests(void)
 {
 	static bool initialized;
@@ -2828,6 +2881,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");
@@ -2873,6 +2928,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.20.1


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

* [PATCH v5 7/9] trace-cmd: Implemented new API tracecmd_add_option_v()
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
                   ` (5 preceding siblings ...)
  2019-03-08 13:44 ` [PATCH v5 6/9] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 8/9] trace-cmd: Implemented new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 9/9] trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm, using vsock events Tzvetomir Stoyanov
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

This patch implements a new tracecmd API, tracecmd_add_option_v()
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 +
 tracecmd/trace-output.c          | 117 ++++++++++++++++++++++++++-----
 3 files changed, 106 insertions(+), 17 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 3f0ab4f..023b701 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -245,11 +245,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 3f2ae80..9705b51 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 "trace-seq.h"
 
diff --git a/tracecmd/trace-output.c b/tracecmd/trace-output.c
index 33d6ce3..d57539c 100644
--- a/tracecmd/trace-output.c
+++ b/tracecmd/trace-output.c
@@ -883,21 +883,23 @@ 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.
@@ -906,32 +908,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 = 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);
@@ -979,6 +1012,56 @@ 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;
+
+	return 0;
+}
+
 int tracecmd_update_option(struct tracecmd_output *handle,
 			   struct tracecmd_option *option, int size,
 			   const void *data)
-- 
2.20.1


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

* [PATCH v5 8/9] trace-cmd: Implemented new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
                   ` (6 preceding siblings ...)
  2019-03-08 13:44 ` [PATCH v5 7/9] trace-cmd: Implemented new API tracecmd_add_option_v() Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-08 13:44 ` [PATCH v5 9/9] trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm, using vsock events Tzvetomir Stoyanov
  8 siblings, 0 replies; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

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:
 first 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 |   1 +
 lib/trace-cmd/trace-input.c   | 119 +++++++++++++++++++++++++++++++++-
 2 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 023b701..cea6bb9 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -82,6 +82,7 @@ enum {
 	TRACECMD_OPTION_HOOK,
 	TRACECMD_OPTION_OFFSET,
 	TRACECMD_OPTION_CPUCOUNT,
+	TRACECMD_OPTION_TIME_SHIFT,
 };
 
 enum {
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index 0a6e820..bfda4c2 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -75,6 +75,11 @@ struct input_buffer_instance {
 	size_t			offset;
 };
 
+struct ts_offset_corr {
+	long long	time;
+	long long	ofsset;
+};
+
 struct tracecmd_input {
 	struct tep_handle	*pevent;
 	struct tep_plugin_list	*plugin_list;
@@ -92,6 +97,8 @@ struct tracecmd_input {
 	bool			use_pipe;
 	struct cpu_data 	*cpu_data;
 	long long		ts_offset;
+	int			ts_corr_count;
+	struct ts_offset_corr	*ts_corr;
 	double			ts2secs;
 	char *			cpustats;
 	char *			uname;
@@ -1028,6 +1035,68 @@ 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_corr *min,
+			  struct ts_offset_corr *max)
+{
+	long long tscor = min->ofsset +
+			(((((long long)ts) - min->time)*
+			(max->ofsset-min->ofsset))/(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)
+{
+	static int min = -1;
+	static int max = -1;
+	int i;
+
+	if (handle->ts_offset)
+		return ts + handle->ts_offset;
+	if (!handle->ts_corr_count || !handle->ts_corr)
+		return ts;
+
+	/* We have only one offset, nothing to calc here */
+	if (handle->ts_corr_count == 1)
+		return ts + handle->ts_corr[0].ofsset;
+
+	/* We have two offsets, nothing to search here */
+	if (handle->ts_corr_count == 2)
+		return timestamp_correction_calc(ts, &handle->ts_corr[0],
+						 &handle->ts_corr[1]);
+
+	/* We have more than two offsets */
+	/* check if current ts fits in the last found time frame */
+	if (min >= 0 && max >= 0 &&
+	    ts >= handle->ts_corr[min].time &&
+	    ts <= handle->ts_corr[max].time) {
+		return timestamp_correction_calc(ts, &handle->ts_corr[min],
+						 &handle->ts_corr[max]);
+	}
+	min = -1;
+	max = -1;
+	for (i = 0; i < handle->ts_corr_count; i++) {
+		if (ts > handle->ts_corr[i].time)
+			min = i;
+		else if (ts <= handle->ts_corr[i].time) {
+			max = i;
+			break;
+		}
+	}
+	if (min >= 0 && max >= 0 && min != max &&
+	    min < handle->ts_corr_count && max < handle->ts_corr_count)
+		return timestamp_correction_calc(ts, &handle->ts_corr[min],
+						 &handle->ts_corr[max]);
+
+	return ts;
+}
+
 /*
  * Page is mapped, now read in the page header info.
  */
@@ -1049,7 +1118,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;
@@ -1776,7 +1845,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;
@@ -2101,6 +2170,34 @@ 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_corr *ts_a = (struct ts_offset_corr *)a;
+	struct ts_offset_corr *ts_b = (struct ts_offset_corr *)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)
+{
+	int i;
+	long long *buf8 = (long long *)buf;
+
+	for (i = 0; i < handle->ts_corr_count; i++) {
+		handle->ts_corr[i].time = tep_read_number(handle->pevent,
+							  buf8+i, 8);
+		handle->ts_corr[i].ofsset = tep_read_number(handle->pevent,
+						buf8+handle->ts_corr_count+i, 8);
+	}
+	qsort(handle->ts_corr,
+	      handle->ts_corr_count, sizeof(struct ts_offset_corr),
+	      tsync_offset_cmp);
+}
+
 static int handle_options(struct tracecmd_input *handle)
 {
 	long long offset;
@@ -2111,6 +2208,7 @@ static int handle_options(struct tracecmd_input *handle)
 	struct input_buffer_instance *buffer;
 	struct hook_list *hook;
 	char *buf;
+	int tsync;
 	int cpus;
 
 	for (;;) {
@@ -2155,6 +2253,23 @@ static int handle_options(struct tracecmd_input *handle)
 			offset = strtoll(buf, NULL, 0);
 			handle->ts_offset += offset;
 			break;
+		case TRACECMD_OPTION_TIME_SHIFT:
+			/*
+			 * 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.
+			 */
+			handle->ts_corr_count = tep_read_number(handle->pevent,
+								buf, 4);
+			tsync = (sizeof(long long)*handle->ts_corr_count);
+			if (size != (4+(2*tsync)))
+				break;
+			handle->ts_corr = malloc(2*tsync);
+			if (!handle->ts_corr)
+				return -ENOMEM;
+			tsync_offset_load(handle, buf+4);
+			break;
 		case TRACECMD_OPTION_CPUSTAT:
 			buf[size-1] = '\n';
 			cpustats = realloc(cpustats, cpustats_size + size + 1);
-- 
2.20.1


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

* [PATCH v5 9/9] trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm, using vsock events.
  2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
                   ` (7 preceding siblings ...)
  2019-03-08 13:44 ` [PATCH v5 8/9] trace-cmd: Implemented new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov
@ 2019-03-08 13:44 ` Tzvetomir Stoyanov
  2019-03-11 11:46   ` Slavomir Kaslev
  8 siblings, 1 reply; 11+ messages in thread
From: Tzvetomir Stoyanov @ 2019-03-08 13:44 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

This is a POC patch, implementing a PTP-like algorithm for syncing timestamps between
host and guest machines, using vsock trace events to catch the host / guest time.

Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
---
 include/trace-cmd/trace-cmd.h  |   9 +
 tracecmd/Makefile              |   1 +
 tracecmd/include/trace-local.h |  50 +++
 tracecmd/trace-msg.c           | 284 ++++++++++++++-
 tracecmd/trace-record.c        |  72 +++-
 tracecmd/trace-timesync.c      | 622 +++++++++++++++++++++++++++++++++
 6 files changed, 1024 insertions(+), 14 deletions(-)
 create mode 100644 tracecmd/trace-timesync.c

diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index cea6bb9..dbfbe14 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -352,6 +352,15 @@ int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int *nr_cpus, int *page_size,
 				 unsigned int **ports, bool *use_fifos);
 
+struct tracecmd_clock_sync;
+
+int tracecmd_msg_rcv_time_sync(struct tracecmd_msg_handle *msg_handle,
+			       struct tracecmd_clock_sync *clock_sync,
+			       long long *toffset, long long *ts_offset);
+int tracecmd_msg_snd_time_sync(struct tracecmd_msg_handle *msg_handle,
+			       struct tracecmd_clock_sync *clock_sync,
+			       long long *toffset, long long *ts_offset);
+
 /* --- Plugin handling --- */
 extern struct tep_plugin_option trace_ftrace_options[];
 
diff --git a/tracecmd/Makefile b/tracecmd/Makefile
index d3e3080..8a73bf7 100644
--- a/tracecmd/Makefile
+++ b/tracecmd/Makefile
@@ -32,6 +32,7 @@ TRACE_CMD_OBJS += trace-list.o
 TRACE_CMD_OBJS += trace-output.o
 TRACE_CMD_OBJS += trace-usage.o
 TRACE_CMD_OBJS += trace-msg.o
+TRACE_CMD_OBJS += trace-timesync.o
 
 ifeq ($(VSOCK_DEFINED), 1)
 TRACE_CMD_OBJS += trace-agent.o
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index 62f5e47..ba0b06d 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -214,6 +214,11 @@ struct buffer_instance {
 	int			port;
 	int			*fds;
 	bool			use_fifos;
+
+	struct tracecmd_clock_sync *clock_sync;
+	int			time_sync_count;
+	long long		*time_sync_ts;
+	long long		*time_sync_ofssets;
 };
 
 extern struct buffer_instance top_instance;
@@ -227,6 +232,37 @@ extern struct buffer_instance *first_instance;
 #define is_agent(instance)	((instance)->flags & BUFFER_FL_AGENT)
 #define is_guest(instance)	((instance)->flags & BUFFER_FL_GUEST)
 
+struct tracecmd_ftrace_param {
+	char	*file;
+	char	*set;
+	char	*reset;
+};
+
+struct tracecmd_event_hit {
+	int			id;
+	int			cpu;
+	int			pid;
+	unsigned long long	ts;
+};
+
+enum clock_sync_conext {
+	CLOCK_KVM_X86_VSOCK_HOST,
+	CLOCK_KVM_X86_VSOCK_GUEST,
+	CLOCK_CONTEXT_MAX,
+};
+
+struct tracecmd_clock_sync {
+	enum clock_sync_conext		clock_context_id;
+	struct tracecmd_ftrace_param	*ftrace_params;
+	struct tracecmd_event_hit	*events;
+	struct tep_handle		*tep;
+	struct buffer_instance		*vinst;
+	unsigned int			local_cid;
+	unsigned int			local_port;
+	unsigned int			remote_cid;
+	unsigned int			remote_port;
+};
+
 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);
@@ -235,6 +271,20 @@ 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);
+
+struct tracecmd_clock_sync*
+tracecmd_clock_context_init(struct tracecmd_msg_handle *msg_handle,
+			    const char *clock_str,
+			    enum clock_sync_conext id);
+void tracecmd_clock_context_free(struct tracecmd_clock_sync *clock_context);
+int tracecmd_clock_find_event(struct tracecmd_clock_sync *clock, int cpu,
+			      struct tracecmd_event_hit *event);
+
+void tracecmd_clock_synch_enable(struct tracecmd_clock_sync *clock_context);
+void tracecmd_clock_synch_disable(struct tracecmd_clock_sync *clock_context);
+
+void sync_time_with_host_v3(struct buffer_instance *instance);
+void sync_time_with_guest_v3(struct buffer_instance *instance);
 void write_tracing_on(struct buffer_instance *instance, int on);
 char *get_instance_dir(struct buffer_instance *instance);
 int write_instance_file(struct buffer_instance *instance,
diff --git a/tracecmd/trace-msg.c b/tracecmd/trace-msg.c
index 065a01e..0f1f071 100644
--- a/tracecmd/trace-msg.c
+++ b/tracecmd/trace-msg.c
@@ -18,16 +18,22 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <time.h>
 #include <arpa/inet.h>
 #include <sys/types.h>
 #include <linux/types.h>
+#include <linux/vm_sockets.h>
 
 #include "trace-cmd-local.h"
 #include "trace-local.h"
 #include "trace-msg.h"
 
+typedef __u16 u16;
+typedef __s16 s16;
 typedef __u32 u32;
 typedef __be32 be32;
+typedef __u64 u64;
+typedef __s64 s64;
 
 static inline void dprint(const char *fmt, ...)
 {
@@ -50,6 +56,9 @@ static inline void dprint(const char *fmt, ...)
 
 unsigned int page_size;
 
+/* Try a few times to get an accurate time sync */
+#define TSYNC_TRIES 300
+
 struct tracecmd_msg_opt {
 	be32 size;
 	be32 opt_cmd;
@@ -76,6 +85,16 @@ struct tracecmd_msg_trace_resp {
 	be32 page_size;
 } __attribute__((packed));
 
+struct tracecmd_msg_tsync_stop {
+	long long offset;
+	long long ts_offset;
+} __attribute__((packed));
+
+struct tracecmd_msg_tsync_pkt {
+	u64 tlocal;
+} __attribute__((packed));
+
+
 struct tracecmd_msg_header {
 	be32	size;
 	be32	cmd;
@@ -83,14 +102,18 @@ struct tracecmd_msg_header {
 } __attribute__((packed));
 
 #define MSG_MAP								\
-	C(CLOSE,	0,	0),					\
-	C(TINIT,	1,	sizeof(struct tracecmd_msg_tinit)),	\
-	C(RINIT,	2,	sizeof(struct tracecmd_msg_rinit)),	\
-	C(SEND_DATA,	3,	0),					\
-	C(FIN_DATA,	4,	0),					\
-	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,	  0,	0),					\
+	C(TINIT,	  1,	sizeof(struct tracecmd_msg_tinit)),	\
+	C(RINIT,	  2,	sizeof(struct tracecmd_msg_rinit)),	\
+	C(SEND_DATA,	  3,	0),					\
+	C(FIN_DATA,	  4,	0),					\
+	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(TSYNC_START,	  8,	0),					\
+	C(TSYNC_STOP,	  9,	sizeof(struct tracecmd_msg_tsync_stop)),\
+	C(TSYNC_REQ,	  10,	sizeof(struct tracecmd_msg_tsync_pkt)),	\
+	C(TSYNC_RESP,	  11,	sizeof(struct tracecmd_msg_tsync_pkt)),
 
 #undef C
 #define C(a,b,c)	MSG_##a = b
@@ -120,10 +143,13 @@ static const char *cmd_to_name(int cmd)
 struct tracecmd_msg {
 	struct tracecmd_msg_header		hdr;
 	union {
-		struct tracecmd_msg_tinit	tinit;
-		struct tracecmd_msg_rinit	rinit;
-		struct tracecmd_msg_trace_req	trace_req;
-		struct tracecmd_msg_trace_resp	trace_resp;
+		struct tracecmd_msg_tinit		tinit;
+		struct tracecmd_msg_rinit		rinit;
+		struct tracecmd_msg_trace_req		trace_req;
+		struct tracecmd_msg_trace_resp		trace_resp;
+		struct tracecmd_msg_tsync_stop		ts_stop;
+		struct tracecmd_msg_tsync_pkt		ts_req;
+		struct tracecmd_msg_tsync_pkt		ts_resp;
 	};
 	union {
 		struct tracecmd_msg_opt		*opt;
@@ -859,6 +885,240 @@ out:
 	return ret;
 }
 
+int tracecmd_msg_rcv_time_sync(struct tracecmd_msg_handle *msg_handle,
+			       struct tracecmd_clock_sync *clock_context,
+			       long long *toffset, long long *ts_offset)
+{
+	struct tracecmd_event_hit event;
+	struct tracecmd_msg msg;
+	int cpu_pid, ret;
+
+	if (clock_context == NULL || msg_handle == NULL)
+		return 0;
+
+	if (toffset)
+		*toffset = 0;
+
+	ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+	if (ret < 0 || ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP)
+		return 0;
+	if (ntohl(msg.hdr.cmd) != MSG_TSYNC_START) {
+		tracecmd_msg_init(MSG_NOT_SUPP, &msg);
+		tracecmd_msg_send(msg_handle->fd, &msg);
+		return 0;
+	}
+
+	tracecmd_msg_init(MSG_TSYNC_START, &msg);
+	tracecmd_msg_send(msg_handle->fd, &msg);
+	tracecmd_clock_synch_enable(clock_context);
+
+	do {
+		memset(&event, 0, sizeof(event));
+		ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+		if (ret < 0 || ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP)
+			break;
+		if (ntohl(msg.hdr.cmd) != MSG_TSYNC_REQ) {
+			tracecmd_msg_init(MSG_NOT_SUPP, &msg);
+			tracecmd_msg_send(msg_handle->fd, &msg);
+			break;
+		}
+		ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+		if (ret < 0 || ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP)
+			break;
+		if (ntohl(msg.hdr.cmd) != MSG_TSYNC_REQ) {
+			tracecmd_msg_init(MSG_NOT_SUPP, &msg);
+			tracecmd_msg_send(msg_handle->fd, &msg);
+			break;
+		}
+		/* Get kvm_exit events related to the corresponding VCPU */
+		cpu_pid = get_guest_vcpu_pid(clock_context->remote_cid,
+					     ntohll(msg.ts_req.tlocal));
+		tracecmd_clock_find_event(clock_context, cpu_pid, &event);
+		tracecmd_msg_init(MSG_TSYNC_RESP, &msg);
+		msg.ts_resp.tlocal = htonll(event.ts);
+		tracecmd_msg_send(msg_handle->fd, &msg);
+	} while (true);
+	tracecmd_clock_synch_disable(clock_context);
+
+	if (ret >= 0 && ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP) {
+		if (toffset)
+			*toffset = ntohll(msg.ts_stop.offset);
+		if (ts_offset)
+			*ts_offset = ntohll(msg.ts_stop.ts_offset);
+	}
+
+	msg_free(&msg);
+	return 0;
+}
+
+//#define TSYNC_DEBUG
+int tracecmd_msg_snd_time_sync(struct tracecmd_msg_handle *msg_handle,
+			       struct tracecmd_clock_sync *clock_context,
+			       long long *toffset, long long *ts_offset)
+{
+	struct tracecmd_event_hit event;
+	struct tracecmd_msg msg_resp;
+	struct tracecmd_msg msg_req;
+	int sync_loop = TSYNC_TRIES;
+	long long min = 0, max = 0;
+	long long  offset_av = 0;
+	long long ts_local = 0;
+	int k = 0, n, ret = 0;
+	long long tresch = 0;
+	long long offset = 0;
+	long long *ts_locals;
+	long long *offsets;
+	long long m_t1 = 0;
+	long long s_t2 = 0;
+	int probes = 0;
+#ifdef TSYNC_DEBUG
+/* Write all ts in a file, used to analyze the raw data */
+	struct timespec tsStart, tsEnd;
+	int zm = 0, zs = 0;
+	long long duration;
+	char buff[256];
+	int iFd;
+#endif
+
+	if (clock_context == NULL || msg_handle == NULL)
+		return 0;
+
+	if (toffset == NULL) {
+		/* No time sync needed, inform the client */
+		msg_req.ts_stop.offset = 0;
+		msg_req.ts_stop.ts_offset = 0;
+		tracecmd_msg_init(MSG_TSYNC_STOP, &msg_req);
+		tracecmd_msg_send(msg_handle->fd, &msg_req);
+		return 0;
+	}
+
+	*toffset = 0;
+	offsets = calloc(sizeof(long long), TSYNC_TRIES);
+	ts_locals = calloc(sizeof(long long), TSYNC_TRIES);
+	if (!offsets || !ts_locals) {
+		free(offsets);
+		free(ts_locals);
+		return 0;
+	}
+
+	tracecmd_msg_init(MSG_TSYNC_START, &msg_req);
+	tracecmd_msg_send(msg_handle->fd, &msg_req);
+	ret = tracecmd_msg_recv(msg_handle->fd, &msg_resp);
+	if (ret < 0 || ntohl(msg_resp.hdr.cmd) != MSG_TSYNC_START) {
+		free(offsets);
+		free(ts_locals);
+		return 0;
+	}
+
+#ifdef TSYNC_DEBUG
+	sprintf(buff, "s-cid%d.txt", clock_context->remote_cid);
+	iFd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644);
+	clock_gettime(CLOCK_MONOTONIC, &tsStart);
+#endif
+	tracecmd_clock_synch_enable(clock_context);
+	do {
+		memset(&msg_resp, 0, sizeof(msg_resp));
+		memset(&event, 0, sizeof(event));
+		tracecmd_msg_init(MSG_TSYNC_REQ, &msg_req);
+		tracecmd_msg_send(msg_handle->fd, &msg_req);
+		/* Get the ts and CPU of the sent event */
+		tracecmd_clock_find_event(clock_context, -1, &event);
+
+		tracecmd_msg_init(MSG_TSYNC_REQ, &msg_req);
+		msg_req.ts_req.tlocal = event.cpu;
+		msg_req.ts_req.tlocal = htonll(msg_req.ts_req.tlocal);
+		tracecmd_msg_send(msg_handle->fd, &msg_req);
+		ret = tracecmd_msg_recv(msg_handle->fd, &msg_resp);
+		if (ret < 0)
+			break;
+		if (ntohl(msg_resp.hdr.cmd) != MSG_TSYNC_RESP) {
+			tracecmd_msg_init(MSG_NOT_SUPP, &msg_req);
+			tracecmd_msg_send(msg_handle->fd, &msg_req);
+			break;
+		}
+		m_t1 = event.ts;
+		s_t2 = htonll(msg_resp.ts_resp.tlocal); /* Client rcv ts */
+#ifdef TSYNC_DEBUG
+		if (!s_t2)
+			zs++;
+		if (!m_t1)
+			zm++;
+#endif
+		if (!s_t2 || !m_t1)
+			continue;
+		offsets[probes] = s_t2 - m_t1;
+		ts_locals[probes] = m_t1;
+		offset_av += offsets[probes];
+		if (!min || min > llabs(offsets[probes]))
+			min = llabs(offsets[probes]);
+		if (!max || max < llabs(offsets[probes]))
+			max = llabs(offsets[probes]);
+		probes++;
+#ifdef TSYNC_DEBUG
+		sprintf(buff, "%lld %lld\n", m_t1, s_t2);
+		write(iFd, buff, strlen(buff));
+#endif
+	} while (--sync_loop);
+
+#ifdef TSYNC_DEBUG
+	clock_gettime(CLOCK_MONOTONIC, &tsEnd);
+	close(iFd);
+#endif
+	tracecmd_clock_synch_disable(clock_context);
+
+	if (probes)
+		offset_av /= (long long)probes;
+	tresch = (long long)((max - min)/10);
+	for (n = 0; n < TSYNC_TRIES; n++) {
+		/* filter the offsets with deviation up to 10% */
+		if (offsets[n] &&
+		    llabs(offsets[n] - offset_av) < tresch) {
+			offset += offsets[n];
+			k++;
+		}
+	}
+	if (k)
+		offset /= (long long)k;
+	tresch = 0;
+	for (n = 0; n < TSYNC_TRIES; n++) {
+		if (offsets[n] &&
+		    (!tresch || tresch > llabs(offset-offsets[n]))) {
+			tresch = llabs(offset-offsets[n]);
+			ts_local = ts_locals[n];
+		}
+	}
+
+	tracecmd_msg_init(MSG_TSYNC_STOP, &msg_req);
+	msg_req.ts_stop.offset = htonll(offset);
+	msg_req.ts_stop.ts_offset = htonll(ts_local);
+	tracecmd_msg_send(msg_handle->fd, &msg_req);
+
+	msg_free(&msg_req);
+	msg_free(&msg_resp);
+	free(offsets);
+	free(ts_locals);
+
+	if (toffset)
+		*toffset = offset;
+	if (ts_offset)
+		*ts_offset = ts_local;
+
+#ifdef TSYNC_DEBUG
+	duration = tsEnd.tv_sec * 1000000000LL;
+	duration += tsEnd.tv_nsec;
+	duration -= (tsStart.tv_sec * 1000000000LL);
+	duration -= tsStart.tv_nsec;
+
+	printf("\n selected: %lld (in %lld ns), used %s clock, %d probes\n\r",
+		offset, duration,
+		clock_context->vinst->clock?clock_context->vinst->clock:"default",
+		probes);
+	printf("\t good probes: %d / %d, threshold %lld ns, Zm %d, Zs %d\n\r",
+			k, TSYNC_TRIES, tresch, zm, zs);
+#endif
+	return 0;
+}
+
 static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
 			   unsigned int *ports, bool use_fifos)
 {
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index d9e9c69..f07eb4d 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -210,6 +210,8 @@ struct common_record_context {
 	char *date2ts;
 	char *max_graph_depth;
 	int data_flags;
+	int time_shift_count;
+	struct tracecmd_option *time_shift_option;
 
 	int record_all;
 	int total_disable;
@@ -649,11 +651,20 @@ static void tell_guests_to_stop(void)
 	for_all_instances(instance) {
 		if (is_guest(instance)) {
 			tracecmd_msg_send_close_msg(instance->msg_handle);
-			tracecmd_msg_handle_close(instance->msg_handle);
 		}
 	}
 }
 
+static void close_guests_handle(void)
+{
+	struct buffer_instance *instance;
+
+	for_all_instances(instance) {
+		if (is_guest(instance))
+			tracecmd_msg_handle_close(instance->msg_handle);
+	}
+}
+
 static void stop_threads(enum trace_type type)
 {
 	struct timeval tv = { 0, 0 };
@@ -3417,6 +3428,8 @@ static void connect_to_agent(struct buffer_instance *instance)
 
 	/* the msg_handle now points to the guest fd */
 	instance->msg_handle = msg_handle;
+
+	sync_time_with_guest_v3(instance);
 }
 
 static void setup_guest(struct buffer_instance *instance)
@@ -3441,10 +3454,13 @@ 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;
 
+	sync_time_with_host_v3(instance);
+
 	network_handle = tracecmd_create_init_fd_msg(instance->msg_handle,
 						     listed_events);
 	add_options(network_handle, ctx);
@@ -5619,6 +5635,42 @@ static bool has_local_instances(void)
 	return false;
 }
 
+//#define TSYNC_DEBUG
+static void write_guest_time_shift(struct buffer_instance *instance)
+{
+	struct tracecmd_output *handle;
+	struct iovec vector[3];
+	char *file;
+	int fd;
+
+	if (!instance->time_sync_count)
+		return;
+
+	file = get_guest_file(output_file, instance->name);
+	fd = open(file, O_RDWR);
+	if (fd < 0)
+		die("error opening %s", file);
+	put_temp_file(file);
+	handle = tracecmd_get_output_handle_fd(fd);
+	vector[0].iov_len = sizeof(instance->time_sync_count);
+	vector[0].iov_base = &instance->time_sync_count;
+	vector[1].iov_len = sizeof(long long) * instance->time_sync_count;
+	vector[1].iov_base = instance->time_sync_ts;
+	vector[2].iov_len = sizeof(long long) * instance->time_sync_count;
+	vector[2].iov_base = instance->time_sync_ofssets;
+	tracecmd_add_option_v(handle, TRACECMD_OPTION_TIME_SHIFT, vector, 3);
+	tracecmd_append_options(handle);
+	tracecmd_output_close(handle);
+#ifdef TSYNC_DEBUG
+	if (instance->time_sync_count > 1)
+		printf("\n\rDetected %lld ns ts offset drift in %lld ns for guest %s\n\r",
+			instance->time_sync_ofssets[instance->time_sync_count-1] -
+			instance->time_sync_ofssets[0],
+			instance->time_sync_ts[instance->time_sync_count-1]-
+			instance->time_sync_ts[0], instance->name);
+#endif
+}
+
 /*
  * This function contains common code for the following commands:
  * record, start, stream, profile.
@@ -5738,6 +5790,22 @@ static void record_trace(int argc, char **argv,
 	if (!latency)
 		wait_threads();
 
+	if (ctx->instance && is_agent(ctx->instance)) {
+		sync_time_with_host_v3(ctx->instance);
+		tracecmd_clock_context_free(ctx->instance->clock_sync);
+		ctx->instance->clock_sync = NULL;
+	} else {
+		for_all_instances(instance) {
+			if (is_guest(instance)) {
+				sync_time_with_guest_v3(instance);
+				write_guest_time_shift(instance);
+				tracecmd_clock_context_free(instance->clock_sync);
+				instance->clock_sync = NULL;
+			}
+		}
+	}
+	close_guests_handle();
+
 	if (IS_RECORD(ctx)) {
 		record_data(ctx);
 		delete_thread_data();
diff --git a/tracecmd/trace-timesync.c b/tracecmd/trace-timesync.c
new file mode 100644
index 0000000..f8366ed
--- /dev/null
+++ b/tracecmd/trace-timesync.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tstoyanov@vmware.com>
+ *
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <linux/vm_sockets.h>
+#include "trace-local.h"
+
+static int clock_sync_x86_host_init(struct tracecmd_clock_sync *clock_context);
+static int clock_sync_x86_host_free(struct tracecmd_clock_sync *clock_context);
+static int clock_sync_x86_host_find_events(struct tracecmd_clock_sync *clock,
+				    int cpu, struct tracecmd_event_hit *event);
+static int clock_sync_x86_guest_init(struct tracecmd_clock_sync *clock_context);
+static int clock_sync_x86_guest_free(struct tracecmd_clock_sync *clock_context);
+static int clock_sync_x86_guest_find_events(struct tracecmd_clock_sync *clock,
+					    int pid,
+					    struct tracecmd_event_hit *event);
+
+struct tracecmd_event_descr {
+	char			*system;
+	char			*name;
+};
+
+struct {
+	char *systems[3];
+	struct tracecmd_ftrace_param ftrace_params[5];
+	struct tracecmd_event_descr events[3];
+	int (*clock_sync_init)(struct tracecmd_clock_sync *clock_context);
+	int (*clock_sync_free)(struct tracecmd_clock_sync *clock_context);
+	int (*clock_sync_find_events)(struct tracecmd_clock_sync *clock_context,
+				      int pid,
+				      struct tracecmd_event_hit *event);
+} static clock_sync[CLOCK_CONTEXT_MAX] = {
+	{ /* CLOCK_KVM_X86_VSOCK_HOST */
+	  .systems = {"vsock", "ftrace", NULL},
+	  .ftrace_params = {
+	  {"set_ftrace_filter", "vmx_read_l1_tsc_offset\nsvm_read_l1_tsc_offset", "\0"},
+	  {"current_tracer", "function", "nop"},
+	  {"events/vsock/virtio_transport_recv_pkt/enable", "1", "0"},
+	  {"events/vsock/virtio_transport_recv_pkt/filter", NULL, "\0"},
+	  {NULL, NULL, NULL} },
+	  .events = {
+	  {.system = "ftrace", .name = "function"},
+	  {.system = "vsock", .name = "virtio_transport_recv_pkt"},
+	  {.system = NULL, .name = NULL} },
+	 clock_sync_x86_host_init,
+	 clock_sync_x86_host_free,
+	 clock_sync_x86_host_find_events,
+	},
+
+	{ /* CLOCK_KVM_X86_VSOCK_GUEST */
+	 .systems = { "vsock", "ftrace", NULL},
+	 .ftrace_params = {
+	  {"set_ftrace_filter", "vp_notify", "\0"},
+	  {"current_tracer", "function", "nop"},
+	  {"events/vsock/virtio_transport_alloc_pkt/enable", "1", "0"},
+	  {"events/vsock/virtio_transport_alloc_pkt/filter", NULL, "\0"},
+	  {NULL, NULL, NULL},
+	  },
+	  .events = {
+	  {.system = "vsock", .name = "virtio_transport_alloc_pkt"},
+	  {.system = "ftrace", .name = "function"},
+	  {.system = NULL, .name = NULL}
+	 },
+	  clock_sync_x86_guest_init,
+	  clock_sync_x86_guest_free,
+	  clock_sync_x86_guest_find_events,
+	}
+};
+
+static int clock_sync_x86_host_init(struct tracecmd_clock_sync *clock_context)
+{
+	char vsock_filter[255];
+
+	snprintf(vsock_filter, 255,
+		"src_cid==%u && src_port==%u && dst_cid==%u && dst_port==%u && len!=0",
+		clock_context->remote_cid, clock_context->remote_port,
+		clock_context->local_cid, clock_context->local_port);
+
+	clock_context->ftrace_params[3].set = strdup(vsock_filter);
+	return 1;
+}
+
+static int clock_sync_x86_host_free(struct tracecmd_clock_sync *clock_context)
+{
+	free(clock_context->ftrace_params[3].set);
+	clock_context->ftrace_params[3].set = NULL;
+	return 1;
+}
+
+static int clock_sync_x86_guest_init(struct tracecmd_clock_sync *clock_context)
+{
+	char vsock_filter[255];
+
+	snprintf(vsock_filter, 255,
+		"src_cid==%u && src_port==%u && dst_cid==%u && dst_port==%u && len!=0",
+		clock_context->local_cid, clock_context->local_port,
+		clock_context->remote_cid, clock_context->remote_port);
+
+	clock_context->ftrace_params[3].set = strdup(vsock_filter);
+	return 1;
+}
+
+static int clock_sync_x86_guest_free(struct tracecmd_clock_sync *clock_context)
+{
+	free(clock_context->ftrace_params[3].set);
+	clock_context->ftrace_params[3].set = NULL;
+	return 1;
+}
+
+#define EVENTS_CHUNK	10
+static int
+get_events_in_page(struct tep_handle *tep, void *page,
+		    int size, int cpu, struct tracecmd_event_hit **events,
+		    int *events_count, int *events_size)
+{
+	struct tracecmd_event_hit *events_array = NULL;
+	struct tep_record *last_record = NULL;
+	struct tep_event *event = NULL;
+	struct tep_record *record;
+	int id, cnt = 0;
+
+	if (size <= 0)
+		return 0;
+
+	if (*events == NULL) {
+		*events = malloc(EVENTS_CHUNK*sizeof(struct tracecmd_event_hit));
+		*events_size = EVENTS_CHUNK;
+		*events_count = 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_data_event_from_type(tep, id);
+		if (event) {
+			if (*events_count >= *events_size) {
+				events_array = realloc(*events,
+					(*events_size + EVENTS_CHUNK)*
+					sizeof(struct tracecmd_event_hit));
+				if (events_array) {
+					*events = events_array;
+					(*events_size) += EVENTS_CHUNK;
+				}
+			}
+
+			if (*events_count < *events_size) {
+				(*events)[*events_count].ts = record->ts;
+				(*events)[*events_count].cpu = cpu;
+				(*events)[*events_count].id = id;
+				(*events)[*events_count].pid = tep_data_pid(tep, record);
+				(*events_count)++;
+				cnt++;
+			}
+		}
+		last_record = record;
+	}
+	free_record(last_record);
+
+	return cnt;
+}
+
+static int sync_events_cmp(const void *a, const void *b)
+{
+	const struct tracecmd_event_hit *ea = (const struct tracecmd_event_hit *)a;
+	const struct tracecmd_event_hit *eb = (const struct tracecmd_event_hit *)b;
+
+	if (ea->ts > eb->ts)
+		return 1;
+	if (ea->ts < eb->ts)
+		return -1;
+	return 0;
+}
+
+static int find_sync_events(struct tep_handle *pevent,
+			    struct tracecmd_event_hit *recorded,
+			    int rsize, struct tracecmd_event_hit *events)
+{
+	int i = 0, j = 0;
+
+	while (i < rsize) {
+		if (!events[j].ts && events[j].id == recorded[i].id &&
+		    (events[j].pid < 0 || events[j].pid == recorded[i].pid)) {
+			events[j].cpu = recorded[i].cpu;
+			events[j].ts = recorded[i].ts;
+			j++;
+		} else if (j > 0 && events[j-1].id == recorded[i].id &&
+			  (events[j-1].pid < 0 || events[j-1].pid == recorded[i].pid)) {
+			events[j-1].cpu = recorded[i].cpu;
+			events[j-1].ts = recorded[i].ts;
+		}
+		i++;
+	}
+	return j;
+}
+
+//#define TSYNC_RBUFFER_DEBUG
+static int find_raw_events(struct tep_handle *tep,
+		    struct buffer_instance *instance,
+		    struct tracecmd_event_hit *events)
+{
+	struct tracecmd_event_hit *events_array = NULL;
+	int events_count = 0;
+	int events_size = 0;
+	struct dirent *dent;
+	unsigned int p_size;
+	int ts = 0;
+	void *page;
+	char *path;
+	char *file;
+	DIR *dir;
+	int cpu;
+	int len;
+	int fd;
+	int r;
+
+	p_size = getpagesize();
+#ifdef TSYNC_RBUFFER_DEBUG
+	file = get_instance_file(instance, "trace");
+	if (!file)
+		return ts;
+	{
+		char *buf = NULL;
+		FILE *fp;
+		size_t n;
+		int r;
+
+		printf("Events:\n\r");
+		fp = fopen(file, "r");
+		while ((r = getline(&buf, &n, fp)) >= 0) {
+
+			if (buf[0] != '#')
+				printf("%s", buf);
+			free(buf);
+			buf = NULL;
+		}
+		fclose(fp);
+	}
+	tracecmd_put_tracing_file(file);
+#endif /* TSYNC_RBUFFER_DEBUG */
+	path = get_instance_file(instance, "per_cpu");
+	if (!path)
+		return ts;
+
+	dir = opendir(path);
+	if (!dir)
+		goto out;
+
+	len = strlen(path);
+	file = malloc(len + strlen("trace_pipe_raw") + 32);
+	page = malloc(p_size);
+	if (!file || !page)
+		die("Failed to allocate time_stamp info");
+
+	while ((dent = readdir(dir))) {
+
+		const char *name = dent->d_name;
+
+		if (strncmp(name, "cpu", 3) != 0)
+			continue;
+		cpu = atoi(&name[3]);
+		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,
+						   &events_array, &events_count,
+						   &events_size);
+			}
+		} while (r > 0);
+		close(fd);
+	}
+	qsort(events_array, events_count, sizeof(*events_array), sync_events_cmp);
+	r = find_sync_events(tep, events_array, events_count, events);
+#ifdef TSYNC_RBUFFER_DEBUG
+	len = 0;
+	while (events[len].id) {
+		printf("Found %d @ cpu %d: %lld pid %d\n\r",
+			events[len].id,  events[len].cpu,
+			events[len].ts, events[len].pid);
+		len++;
+	}
+#endif
+
+	free(events_array);
+	free(file);
+	free(page);
+	closedir(dir);
+
+ out:
+	tracecmd_put_tracing_file(path);
+	return r;
+}
+
+static int clock_sync_x86_host_find_events(struct tracecmd_clock_sync *clock,
+					   int pid,
+					   struct tracecmd_event_hit *event)
+{
+	int ret;
+
+	clock->events[0].pid = pid;
+	ret = find_raw_events(clock->tep, clock->vinst, clock->events);
+	event->ts = clock->events[0].ts;
+	event->cpu = clock->events[0].cpu;
+	return ret;
+
+}
+
+static int clock_sync_x86_guest_find_events(struct tracecmd_clock_sync *clock,
+					    int pid,
+					    struct tracecmd_event_hit *event)
+{
+	int ret;
+
+	ret = find_raw_events(clock->tep, clock->vinst, clock->events);
+	event->ts = clock->events[1].ts;
+	event->cpu = clock->events[0].cpu;
+	return ret;
+
+}
+
+static void tracecmd_clock_sync_reset(struct tracecmd_clock_sync *clock_context)
+{
+	int i = 0;
+
+	while (clock_context->events[i].id) {
+		clock_context->events[i].cpu = 0;
+		clock_context->events[i].ts = 0;
+		clock_context->events[i].pid = -1;
+		i++;
+	}
+}
+
+int tracecmd_clock_find_event(struct tracecmd_clock_sync *clock, int pid,
+			      struct tracecmd_event_hit *event)
+{
+	int ret = 0;
+	int id;
+
+	if (clock == NULL ||
+	    clock->clock_context_id >= CLOCK_CONTEXT_MAX)
+		return 0;
+
+	id = clock->clock_context_id;
+	if (clock_sync[id].clock_sync_find_events)
+		ret = clock_sync[id].clock_sync_find_events(clock, pid, event);
+
+	tracecmd_clock_sync_reset(clock);
+	return ret;
+}
+
+static void clock_context_copy(struct tracecmd_clock_sync *clock_context,
+			       struct tracecmd_ftrace_param *params,
+			       struct tracecmd_event_descr *events)
+{
+	int i;
+
+	i = 0;
+	while (params[i].file)
+		i++;
+	clock_context->ftrace_params = calloc(i+1, sizeof(struct tracecmd_ftrace_param));
+	i = 0;
+	while (params[i].file) {
+		clock_context->ftrace_params[i].file = strdup(params[i].file);
+		if (params[i].set)
+			clock_context->ftrace_params[i].set = strdup(params[i].set);
+		if (params[i].reset)
+			clock_context->ftrace_params[i].reset = strdup(params[i].reset);
+		i++;
+	}
+
+	i = 0;
+	while (events[i].name)
+		i++;
+	clock_context->events = calloc(i+1, sizeof(struct tracecmd_event_hit));
+}
+
+void trace_instance_reset(struct buffer_instance *vinst)
+{
+	write_instance_file(vinst, "trace", "\0", NULL);
+}
+
+static struct buffer_instance *
+clock_synch_create_instance(const char *clock, unsigned int cid)
+{
+	struct buffer_instance *vinst;
+	char inst_name[256];
+
+	snprintf(inst_name, 256, "clock_synch-%d", cid);
+
+	vinst = create_instance(strdup(inst_name));
+	tracecmd_init_instance(vinst);
+	vinst->cpu_count = tracecmd_local_cpu_count();
+	tracecmd_make_instance(vinst);
+	trace_instance_reset(vinst);
+	if (clock)
+		vinst->clock = strdup(clock);
+	tracecmd_set_clock(vinst);
+	return vinst;
+}
+
+static struct tep_handle *clock_synch_get_tep(struct buffer_instance *instance,
+					      char **systems)
+{
+	struct tep_handle *tep = NULL;
+	char *path;
+
+	path = get_instance_dir(instance);
+	tep = tracecmd_local_events_system(path, systems);
+	tracecmd_put_tracing_file(path);
+
+	tep_set_file_bigendian(tep, tracecmd_host_bigendian());
+	tep_set_host_bigendian(tep, tracecmd_host_bigendian());
+
+	return tep;
+}
+
+static void 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);
+
+	if (lcid || lport) {
+		memset(&addr, 0, sizeof(addr));
+		if (getsockname(fd, (struct sockaddr *)&addr, &addr_len))
+			return;
+		if (addr.svm_family != AF_VSOCK)
+			return;
+		if (lport)
+			*lport = addr.svm_port;
+		if (lcid)
+			*lcid = addr.svm_cid;
+	}
+
+	if (rcid || rport) {
+		memset(&addr, 0, sizeof(addr));
+		addr_len = sizeof(addr);
+		if (getpeername(fd, (struct sockaddr *)&addr, &addr_len))
+			return;
+		if (addr.svm_family != AF_VSOCK)
+			return;
+
+		if (rport)
+			*rport = addr.svm_port;
+		if (rcid)
+			*rcid = addr.svm_cid;
+	}
+}
+
+struct tracecmd_clock_sync*
+tracecmd_clock_context_init(struct tracecmd_msg_handle *msg_handle,
+			    const char *clock_str,
+			    enum clock_sync_conext id)
+{
+	struct tracecmd_clock_sync *clock_context;
+	struct tep_event *event;
+	unsigned int i = 0;
+
+	switch (id) {
+#ifdef VSOCK
+	case CLOCK_KVM_X86_VSOCK_HOST:
+	case CLOCK_KVM_X86_VSOCK_GUEST:
+		break;
+#endif
+	default: /* not supported clock sync context */
+		return NULL;
+	}
+
+	if (id >= CLOCK_CONTEXT_MAX || NULL == msg_handle)
+		return NULL;
+	clock_context = calloc(1, sizeof(struct tracecmd_clock_sync));
+	if (!clock_context)
+		return NULL;
+	clock_context->clock_context_id = id;
+	clock_context_copy(clock_context,
+			   clock_sync[id].ftrace_params, clock_sync[id].events);
+
+	get_vsocket_params(msg_handle->fd,
+			   &(clock_context->local_cid),
+			   &(clock_context->local_port),
+			   &(clock_context->remote_cid),
+			   &(clock_context->remote_port));
+
+	if (clock_sync[id].clock_sync_init)
+		clock_sync[id].clock_sync_init(clock_context);
+
+	clock_context->vinst = clock_synch_create_instance(clock_str, clock_context->remote_cid);
+	clock_context->tep = clock_synch_get_tep(clock_context->vinst,
+						 clock_sync[id].systems);
+	i = 0;
+	while (clock_sync[id].events[i].name) {
+		event = tep_find_event_by_name(clock_context->tep,
+					       clock_sync[id].events[i].system,
+					       clock_sync[id].events[i].name);
+		if (!event)
+			break;
+		clock_context->events[i].id = event->id;
+		i++;
+	}
+	return clock_context;
+
+}
+
+void tracecmd_clock_context_free(struct tracecmd_clock_sync *clock_context)
+{
+	int i;
+
+	if (clock_context == NULL ||
+	    clock_context->clock_context_id >= CLOCK_CONTEXT_MAX)
+		return;
+	if (clock_sync[clock_context->clock_context_id].clock_sync_free)
+		clock_sync[clock_context->clock_context_id].clock_sync_free(clock_context);
+
+	i = 0;
+	while (clock_context->ftrace_params[i].file) {
+		free(clock_context->ftrace_params[i].file);
+		free(clock_context->ftrace_params[i].set);
+		free(clock_context->ftrace_params[i].reset);
+		i++;
+	}
+	free(clock_context->ftrace_params);
+	free(clock_context->events);
+	tracecmd_remove_instance(clock_context->vinst);
+	/* todo: clean up the instance */
+	tep_free(clock_context->tep);
+	free(clock_context);
+}
+
+void sync_time_with_host_v3(struct buffer_instance *instance)
+{
+	long long ts_local = 0;
+	long long toffset = 0;
+
+	if (instance->clock_sync == NULL)
+		instance->clock_sync = tracecmd_clock_context_init(instance->msg_handle,
+					instance->clock, CLOCK_KVM_X86_VSOCK_GUEST);
+
+	tracecmd_msg_snd_time_sync(instance->msg_handle, instance->clock_sync,
+				   &toffset, &ts_local);
+}
+
+void sync_time_with_guest_v3(struct buffer_instance *instance)
+{
+	long long ts_local = 0;
+	long long toffset = 0;
+	long long *sync_array_ts;
+	long long *sync_array_ofs;
+
+	if (instance->clock_sync == NULL)
+		instance->clock_sync = tracecmd_clock_context_init(instance->msg_handle,
+						top_instance.clock, CLOCK_KVM_X86_VSOCK_HOST);
+
+	tracecmd_msg_rcv_time_sync(instance->msg_handle,
+				   instance->clock_sync, &toffset, &ts_local);
+
+	sync_array_ts = realloc(instance->time_sync_ts,
+			    (instance->time_sync_count+1)*sizeof(long long));
+	sync_array_ofs = realloc(instance->time_sync_ofssets,
+			    (instance->time_sync_count+1)*sizeof(long long));
+
+	if (sync_array_ts && sync_array_ofs) {
+		sync_array_ts[instance->time_sync_count] = ts_local;
+		sync_array_ofs[instance->time_sync_count] = toffset;
+		instance->time_sync_count++;
+		instance->time_sync_ts = sync_array_ts;
+		instance->time_sync_ofssets = sync_array_ofs;
+
+	} else {
+		free(sync_array_ts);
+		free(sync_array_ofs);
+	}
+}
+
+static void set_clock_synch_events(struct buffer_instance *instance,
+				   struct tracecmd_ftrace_param *params,
+				   bool enable)
+{
+	int i = 0;
+
+	if (!enable)
+		write_tracing_on(instance, 0);
+
+	while (params[i].file) {
+		if (enable && params[i].set) {
+			write_instance_file(instance, params[i].file,
+					params[i].set, NULL);
+		}
+		if (!enable && params[i].reset)
+			write_instance_file(instance, params[i].file,
+					params[i].reset, NULL);
+		i++;
+	}
+
+	if (enable)
+		write_tracing_on(instance, 1);
+}
+
+void tracecmd_clock_synch_enable(struct tracecmd_clock_sync *clock_context)
+{
+	set_clock_synch_events(clock_context->vinst,
+			       clock_context->ftrace_params, true);
+}
+
+void tracecmd_clock_synch_disable(struct tracecmd_clock_sync *clock_context)
+{
+	set_clock_synch_events(clock_context->vinst,
+			       clock_context->ftrace_params, false);
+}
-- 
2.20.1


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

* Re: [PATCH v5 9/9] trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm, using vsock events.
  2019-03-08 13:44 ` [PATCH v5 9/9] trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm, using vsock events Tzvetomir Stoyanov
@ 2019-03-11 11:46   ` Slavomir Kaslev
  0 siblings, 0 replies; 11+ messages in thread
From: Slavomir Kaslev @ 2019-03-11 11:46 UTC (permalink / raw)
  To: Tzvetomir Stoyanov; +Cc: rostedt, linux-trace-devel

Some minor comments inline since we discussed the more major issues with patches 8 and 9 offline already.

On Fri, Mar 08, 2019 at 03:44:23PM +0200, Tzvetomir Stoyanov wrote:
> This is a POC patch, implementing a PTP-like algorithm for syncing timestamps between
> host and guest machines, using vsock trace events to catch the host / guest time.

This patch needs a new commit message since it's not doing anything resembling PTP anymore.

> 
> Signed-off-by: Tzvetomir Stoyanov <tstoyanov@vmware.com>
> ---
>  include/trace-cmd/trace-cmd.h  |   9 +
>  tracecmd/Makefile              |   1 +
>  tracecmd/include/trace-local.h |  50 +++
>  tracecmd/trace-msg.c           | 284 ++++++++++++++-
>  tracecmd/trace-record.c        |  72 +++-
>  tracecmd/trace-timesync.c      | 622 +++++++++++++++++++++++++++++++++
>  6 files changed, 1024 insertions(+), 14 deletions(-)
>  create mode 100644 tracecmd/trace-timesync.c
> 
> diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
> index cea6bb9..dbfbe14 100644
> --- a/include/trace-cmd/trace-cmd.h
> +++ b/include/trace-cmd/trace-cmd.h
> @@ -352,6 +352,15 @@ int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
>  				 int *nr_cpus, int *page_size,
>  				 unsigned int **ports, bool *use_fifos);
>  
> +struct tracecmd_clock_sync;
> +
> +int tracecmd_msg_rcv_time_sync(struct tracecmd_msg_handle *msg_handle,
> +			       struct tracecmd_clock_sync *clock_sync,
> +			       long long *toffset, long long *ts_offset);
> +int tracecmd_msg_snd_time_sync(struct tracecmd_msg_handle *msg_handle,
> +			       struct tracecmd_clock_sync *clock_sync,
> +			       long long *toffset, long long *ts_offset);
> +
>  /* --- Plugin handling --- */
>  extern struct tep_plugin_option trace_ftrace_options[];
>  
> diff --git a/tracecmd/Makefile b/tracecmd/Makefile
> index d3e3080..8a73bf7 100644
> --- a/tracecmd/Makefile
> +++ b/tracecmd/Makefile
> @@ -32,6 +32,7 @@ TRACE_CMD_OBJS += trace-list.o
>  TRACE_CMD_OBJS += trace-output.o
>  TRACE_CMD_OBJS += trace-usage.o
>  TRACE_CMD_OBJS += trace-msg.o
> +TRACE_CMD_OBJS += trace-timesync.o
>  
>  ifeq ($(VSOCK_DEFINED), 1)
>  TRACE_CMD_OBJS += trace-agent.o
> diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
> index 62f5e47..ba0b06d 100644
> --- a/tracecmd/include/trace-local.h
> +++ b/tracecmd/include/trace-local.h
> @@ -214,6 +214,11 @@ struct buffer_instance {
>  	int			port;
>  	int			*fds;
>  	bool			use_fifos;
> +
> +	struct tracecmd_clock_sync *clock_sync;
> +	int			time_sync_count;
> +	long long		*time_sync_ts;
> +	long long		*time_sync_ofssets;
>  };
>  
>  extern struct buffer_instance top_instance;
> @@ -227,6 +232,37 @@ extern struct buffer_instance *first_instance;
>  #define is_agent(instance)	((instance)->flags & BUFFER_FL_AGENT)
>  #define is_guest(instance)	((instance)->flags & BUFFER_FL_GUEST)
>  
> +struct tracecmd_ftrace_param {
> +	char	*file;
> +	char	*set;
> +	char	*reset;
> +};
> +
> +struct tracecmd_event_hit {
> +	int			id;
> +	int			cpu;
> +	int			pid;
> +	unsigned long long	ts;
> +};
> +
> +enum clock_sync_conext {
> +	CLOCK_KVM_X86_VSOCK_HOST,
> +	CLOCK_KVM_X86_VSOCK_GUEST,
> +	CLOCK_CONTEXT_MAX,
> +};

I don't think those should be exposed since they are not generic and are not used outside of time synchronization code.

> +
> +struct tracecmd_clock_sync {
> +	enum clock_sync_conext		clock_context_id;
> +	struct tracecmd_ftrace_param	*ftrace_params;
> +	struct tracecmd_event_hit	*events;
> +	struct tep_handle		*tep;
> +	struct buffer_instance		*vinst;
> +	unsigned int			local_cid;
> +	unsigned int			local_port;
> +	unsigned int			remote_cid;
> +	unsigned int			remote_port;
> +};
> +
>  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);
> @@ -235,6 +271,20 @@ 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);
> +
> +struct tracecmd_clock_sync*
> +tracecmd_clock_context_init(struct tracecmd_msg_handle *msg_handle,
> +			    const char *clock_str,
> +			    enum clock_sync_conext id);
> +void tracecmd_clock_context_free(struct tracecmd_clock_sync *clock_context);
> +int tracecmd_clock_find_event(struct tracecmd_clock_sync *clock, int cpu,
> +			      struct tracecmd_event_hit *event);
> +
> +void tracecmd_clock_synch_enable(struct tracecmd_clock_sync *clock_context);
> +void tracecmd_clock_synch_disable(struct tracecmd_clock_sync *clock_context);
> +
> +void sync_time_with_host_v3(struct buffer_instance *instance);
> +void sync_time_with_guest_v3(struct buffer_instance *instance);
>  void write_tracing_on(struct buffer_instance *instance, int on);
>  char *get_instance_dir(struct buffer_instance *instance);
>  int write_instance_file(struct buffer_instance *instance,
> diff --git a/tracecmd/trace-msg.c b/tracecmd/trace-msg.c
> index 065a01e..0f1f071 100644
> --- a/tracecmd/trace-msg.c
> +++ b/tracecmd/trace-msg.c
> @@ -18,16 +18,22 @@
>  #include <stdarg.h>
>  #include <string.h>
>  #include <unistd.h>
> +#include <time.h>
>  #include <arpa/inet.h>
>  #include <sys/types.h>
>  #include <linux/types.h>
> +#include <linux/vm_sockets.h>

This last #include is not necessary.

>  
>  #include "trace-cmd-local.h"
>  #include "trace-local.h"
>  #include "trace-msg.h"
>  
> +typedef __u16 u16;
> +typedef __s16 s16;
>  typedef __u32 u32;
>  typedef __be32 be32;
> +typedef __u64 u64;
> +typedef __s64 s64;
>  
>  static inline void dprint(const char *fmt, ...)
>  {
> @@ -50,6 +56,9 @@ static inline void dprint(const char *fmt, ...)
>  
>  unsigned int page_size;
>  
> +/* Try a few times to get an accurate time sync */
> +#define TSYNC_TRIES 300
> +
>  struct tracecmd_msg_opt {
>  	be32 size;
>  	be32 opt_cmd;
> @@ -76,6 +85,16 @@ struct tracecmd_msg_trace_resp {
>  	be32 page_size;
>  } __attribute__((packed));
>  
> +struct tracecmd_msg_tsync_stop {
> +	long long offset;
> +	long long ts_offset;
> +} __attribute__((packed));
> +
> +struct tracecmd_msg_tsync_pkt {
> +	u64 tlocal;
> +} __attribute__((packed));
> +
> +
>  struct tracecmd_msg_header {
>  	be32	size;
>  	be32	cmd;
> @@ -83,14 +102,18 @@ struct tracecmd_msg_header {
>  } __attribute__((packed));
>  
>  #define MSG_MAP								\
> -	C(CLOSE,	0,	0),					\
> -	C(TINIT,	1,	sizeof(struct tracecmd_msg_tinit)),	\
> -	C(RINIT,	2,	sizeof(struct tracecmd_msg_rinit)),	\
> -	C(SEND_DATA,	3,	0),					\
> -	C(FIN_DATA,	4,	0),					\
> -	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,	  0,	0),					\
> +	C(TINIT,	  1,	sizeof(struct tracecmd_msg_tinit)),	\
> +	C(RINIT,	  2,	sizeof(struct tracecmd_msg_rinit)),	\
> +	C(SEND_DATA,	  3,	0),					\
> +	C(FIN_DATA,	  4,	0),					\
> +	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(TSYNC_START,	  8,	0),					\
> +	C(TSYNC_STOP,	  9,	sizeof(struct tracecmd_msg_tsync_stop)),\
> +	C(TSYNC_REQ,	  10,	sizeof(struct tracecmd_msg_tsync_pkt)),	\
> +	C(TSYNC_RESP,	  11,	sizeof(struct tracecmd_msg_tsync_pkt)),
>  
>  #undef C
>  #define C(a,b,c)	MSG_##a = b
> @@ -120,10 +143,13 @@ static const char *cmd_to_name(int cmd)
>  struct tracecmd_msg {
>  	struct tracecmd_msg_header		hdr;
>  	union {
> -		struct tracecmd_msg_tinit	tinit;
> -		struct tracecmd_msg_rinit	rinit;
> -		struct tracecmd_msg_trace_req	trace_req;
> -		struct tracecmd_msg_trace_resp	trace_resp;
> +		struct tracecmd_msg_tinit		tinit;
> +		struct tracecmd_msg_rinit		rinit;
> +		struct tracecmd_msg_trace_req		trace_req;
> +		struct tracecmd_msg_trace_resp		trace_resp;
> +		struct tracecmd_msg_tsync_stop		ts_stop;
> +		struct tracecmd_msg_tsync_pkt		ts_req;
> +		struct tracecmd_msg_tsync_pkt		ts_resp;
>  	};
>  	union {
>  		struct tracecmd_msg_opt		*opt;
> @@ -859,6 +885,240 @@ out:
>  	return ret;
>  }
>  
> +int tracecmd_msg_rcv_time_sync(struct tracecmd_msg_handle *msg_handle,
> +			       struct tracecmd_clock_sync *clock_context,
> +			       long long *toffset, long long *ts_offset)
> +{
> +	struct tracecmd_event_hit event;
> +	struct tracecmd_msg msg;
> +	int cpu_pid, ret;
> +
> +	if (clock_context == NULL || msg_handle == NULL)
> +		return 0;
> +
> +	if (toffset)
> +		*toffset = 0;
> +
> +	ret = tracecmd_msg_recv(msg_handle->fd, &msg);
> +	if (ret < 0 || ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP)
> +		return 0;
> +	if (ntohl(msg.hdr.cmd) != MSG_TSYNC_START) {
> +		tracecmd_msg_init(MSG_NOT_SUPP, &msg);
> +		tracecmd_msg_send(msg_handle->fd, &msg);

Can you switch this below to the helper function?

	tracecmd_msg_send_notsupp(msg_handle);

> +		return 0;
> +	}
> +
> +	tracecmd_msg_init(MSG_TSYNC_START, &msg);
> +	tracecmd_msg_send(msg_handle->fd, &msg);
> +	tracecmd_clock_synch_enable(clock_context);
> +
> +	do {
> +		memset(&event, 0, sizeof(event));
> +		ret = tracecmd_msg_recv(msg_handle->fd, &msg);
> +		if (ret < 0 || ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP)
> +			break;
> +		if (ntohl(msg.hdr.cmd) != MSG_TSYNC_REQ) {
> +			tracecmd_msg_init(MSG_NOT_SUPP, &msg);
> +			tracecmd_msg_send(msg_handle->fd, &msg);
> +			break;
> +		}
> +		ret = tracecmd_msg_recv(msg_handle->fd, &msg);
> +		if (ret < 0 || ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP)
> +			break;
> +		if (ntohl(msg.hdr.cmd) != MSG_TSYNC_REQ) {
> +			tracecmd_msg_init(MSG_NOT_SUPP, &msg);
> +			tracecmd_msg_send(msg_handle->fd, &msg);
> +			break;
> +		}
> +		/* Get kvm_exit events related to the corresponding VCPU */
> +		cpu_pid = get_guest_vcpu_pid(clock_context->remote_cid,
> +					     ntohll(msg.ts_req.tlocal));
> +		tracecmd_clock_find_event(clock_context, cpu_pid, &event);
> +		tracecmd_msg_init(MSG_TSYNC_RESP, &msg);
> +		msg.ts_resp.tlocal = htonll(event.ts);
> +		tracecmd_msg_send(msg_handle->fd, &msg);
> +	} while (true);
> +	tracecmd_clock_synch_disable(clock_context);
> +
> +	if (ret >= 0 && ntohl(msg.hdr.cmd) == MSG_TSYNC_STOP) {
> +		if (toffset)
> +			*toffset = ntohll(msg.ts_stop.offset);
> +		if (ts_offset)
> +			*ts_offset = ntohll(msg.ts_stop.ts_offset);
> +	}
> +
> +	msg_free(&msg);
> +	return 0;
> +}
> +
> +//#define TSYNC_DEBUG
> +int tracecmd_msg_snd_time_sync(struct tracecmd_msg_handle *msg_handle,
> +			       struct tracecmd_clock_sync *clock_context,
> +			       long long *toffset, long long *ts_offset)
> +{
> +	struct tracecmd_event_hit event;
> +	struct tracecmd_msg msg_resp;
> +	struct tracecmd_msg msg_req;
> +	int sync_loop = TSYNC_TRIES;
> +	long long min = 0, max = 0;
> +	long long  offset_av = 0;
> +	long long ts_local = 0;
> +	int k = 0, n, ret = 0;
> +	long long tresch = 0;
> +	long long offset = 0;
> +	long long *ts_locals;
> +	long long *offsets;
> +	long long m_t1 = 0;
> +	long long s_t2 = 0;
> +	int probes = 0;
> +#ifdef TSYNC_DEBUG
> +/* Write all ts in a file, used to analyze the raw data */
> +	struct timespec tsStart, tsEnd;
> +	int zm = 0, zs = 0;
> +	long long duration;
> +	char buff[256];
> +	int iFd;
> +#endif
> +
> +	if (clock_context == NULL || msg_handle == NULL)
> +		return 0;
> +
> +	if (toffset == NULL) {
> +		/* No time sync needed, inform the client */
> +		msg_req.ts_stop.offset = 0;
> +		msg_req.ts_stop.ts_offset = 0;
> +		tracecmd_msg_init(MSG_TSYNC_STOP, &msg_req);
> +		tracecmd_msg_send(msg_handle->fd, &msg_req);
> +		return 0;
> +	}
> +
> +	*toffset = 0;
> +	offsets = calloc(sizeof(long long), TSYNC_TRIES);
> +	ts_locals = calloc(sizeof(long long), TSYNC_TRIES);
> +	if (!offsets || !ts_locals) {
> +		free(offsets);
> +		free(ts_locals);
> +		return 0;
> +	}
> +
> +	tracecmd_msg_init(MSG_TSYNC_START, &msg_req);
> +	tracecmd_msg_send(msg_handle->fd, &msg_req);
> +	ret = tracecmd_msg_recv(msg_handle->fd, &msg_resp);
> +	if (ret < 0 || ntohl(msg_resp.hdr.cmd) != MSG_TSYNC_START) {
> +		free(offsets);
> +		free(ts_locals);
> +		return 0;
> +	}
> +
> +#ifdef TSYNC_DEBUG
> +	sprintf(buff, "s-cid%d.txt", clock_context->remote_cid);
> +	iFd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644);
> +	clock_gettime(CLOCK_MONOTONIC, &tsStart);
> +#endif
> +	tracecmd_clock_synch_enable(clock_context);
> +	do {
> +		memset(&msg_resp, 0, sizeof(msg_resp));
> +		memset(&event, 0, sizeof(event));
> +		tracecmd_msg_init(MSG_TSYNC_REQ, &msg_req);
> +		tracecmd_msg_send(msg_handle->fd, &msg_req);
> +		/* Get the ts and CPU of the sent event */
> +		tracecmd_clock_find_event(clock_context, -1, &event);
> +
> +		tracecmd_msg_init(MSG_TSYNC_REQ, &msg_req);
> +		msg_req.ts_req.tlocal = event.cpu;
> +		msg_req.ts_req.tlocal = htonll(msg_req.ts_req.tlocal);
> +		tracecmd_msg_send(msg_handle->fd, &msg_req);
> +		ret = tracecmd_msg_recv(msg_handle->fd, &msg_resp);
> +		if (ret < 0)
> +			break;
> +		if (ntohl(msg_resp.hdr.cmd) != MSG_TSYNC_RESP) {
> +			tracecmd_msg_init(MSG_NOT_SUPP, &msg_req);
> +			tracecmd_msg_send(msg_handle->fd, &msg_req);
> +			break;
> +		}
> +		m_t1 = event.ts;
> +		s_t2 = htonll(msg_resp.ts_resp.tlocal); /* Client rcv ts */
> +#ifdef TSYNC_DEBUG
> +		if (!s_t2)
> +			zs++;
> +		if (!m_t1)
> +			zm++;
> +#endif
> +		if (!s_t2 || !m_t1)
> +			continue;
> +		offsets[probes] = s_t2 - m_t1;
> +		ts_locals[probes] = m_t1;
> +		offset_av += offsets[probes];
> +		if (!min || min > llabs(offsets[probes]))
> +			min = llabs(offsets[probes]);
> +		if (!max || max < llabs(offsets[probes]))
> +			max = llabs(offsets[probes]);
> +		probes++;
> +#ifdef TSYNC_DEBUG
> +		sprintf(buff, "%lld %lld\n", m_t1, s_t2);
> +		write(iFd, buff, strlen(buff));
> +#endif
> +	} while (--sync_loop);
> +
> +#ifdef TSYNC_DEBUG
> +	clock_gettime(CLOCK_MONOTONIC, &tsEnd);
> +	close(iFd);
> +#endif
> +	tracecmd_clock_synch_disable(clock_context);
> +

Can you split the part that processes the events from the one that collects them and make this function shorter and simpler?

> +	if (probes)
> +		offset_av /= (long long)probes;
> +	tresch = (long long)((max - min)/10);
> +	for (n = 0; n < TSYNC_TRIES; n++) {
> +		/* filter the offsets with deviation up to 10% */
> +		if (offsets[n] &&
> +		    llabs(offsets[n] - offset_av) < tresch) {
> +			offset += offsets[n];
> +			k++;
> +		}
> +	}
> +	if (k)
> +		offset /= (long long)k;
> +	tresch = 0;
> +	for (n = 0; n < TSYNC_TRIES; n++) {
> +		if (offsets[n] &&
> +		    (!tresch || tresch > llabs(offset-offsets[n]))) {
> +			tresch = llabs(offset-offsets[n]);
> +			ts_local = ts_locals[n];
> +		}
> +	}
> +
> +	tracecmd_msg_init(MSG_TSYNC_STOP, &msg_req);
> +	msg_req.ts_stop.offset = htonll(offset);
> +	msg_req.ts_stop.ts_offset = htonll(ts_local);
> +	tracecmd_msg_send(msg_handle->fd, &msg_req);
> +
> +	msg_free(&msg_req);
> +	msg_free(&msg_resp);
> +	free(offsets);
> +	free(ts_locals);
> +
> +	if (toffset)
> +		*toffset = offset;
> +	if (ts_offset)
> +		*ts_offset = ts_local;
> +
> +#ifdef TSYNC_DEBUG
> +	duration = tsEnd.tv_sec * 1000000000LL;
> +	duration += tsEnd.tv_nsec;
> +	duration -= (tsStart.tv_sec * 1000000000LL);
> +	duration -= tsStart.tv_nsec;
> +
> +	printf("\n selected: %lld (in %lld ns), used %s clock, %d probes\n\r",
> +		offset, duration,
> +		clock_context->vinst->clock?clock_context->vinst->clock:"default",
> +		probes);
> +	printf("\t good probes: %d / %d, threshold %lld ns, Zm %d, Zs %d\n\r",
> +			k, TSYNC_TRIES, tresch, zm, zs);
> +#endif
> +	return 0;
> +}
> +
>  static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
>  			   unsigned int *ports, bool use_fifos)
>  {
> diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
> index d9e9c69..f07eb4d 100644
> --- a/tracecmd/trace-record.c
> +++ b/tracecmd/trace-record.c
> @@ -210,6 +210,8 @@ struct common_record_context {
>  	char *date2ts;
>  	char *max_graph_depth;
>  	int data_flags;
> +	int time_shift_count;
> +	struct tracecmd_option *time_shift_option;
>  
>  	int record_all;
>  	int total_disable;
> @@ -649,11 +651,20 @@ static void tell_guests_to_stop(void)
>  	for_all_instances(instance) {
>  		if (is_guest(instance)) {
>  			tracecmd_msg_send_close_msg(instance->msg_handle);
> -			tracecmd_msg_handle_close(instance->msg_handle);
>  		}
>  	}
>  }
>  
> +static void close_guests_handle(void)
> +{
> +	struct buffer_instance *instance;
> +
> +	for_all_instances(instance) {
> +		if (is_guest(instance))
> +			tracecmd_msg_handle_close(instance->msg_handle);
> +	}
> +}
> +
>  static void stop_threads(enum trace_type type)
>  {
>  	struct timeval tv = { 0, 0 };
> @@ -3417,6 +3428,8 @@ static void connect_to_agent(struct buffer_instance *instance)
>  
>  	/* the msg_handle now points to the guest fd */
>  	instance->msg_handle = msg_handle;
> +
> +	sync_time_with_guest_v3(instance);
>  }
>  
>  static void setup_guest(struct buffer_instance *instance)
> @@ -3441,10 +3454,13 @@ 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;
>  
> +	sync_time_with_host_v3(instance);
> +
>  	network_handle = tracecmd_create_init_fd_msg(instance->msg_handle,
>  						     listed_events);
>  	add_options(network_handle, ctx);
> @@ -5619,6 +5635,42 @@ static bool has_local_instances(void)
>  	return false;
>  }
>  
> +//#define TSYNC_DEBUG
> +static void write_guest_time_shift(struct buffer_instance *instance)
> +{
> +	struct tracecmd_output *handle;
> +	struct iovec vector[3];
> +	char *file;
> +	int fd;
> +
> +	if (!instance->time_sync_count)
> +		return;
> +
> +	file = get_guest_file(output_file, instance->name);
> +	fd = open(file, O_RDWR);
> +	if (fd < 0)
> +		die("error opening %s", file);
> +	put_temp_file(file);
> +	handle = tracecmd_get_output_handle_fd(fd);
> +	vector[0].iov_len = sizeof(instance->time_sync_count);
> +	vector[0].iov_base = &instance->time_sync_count;
> +	vector[1].iov_len = sizeof(long long) * instance->time_sync_count;
> +	vector[1].iov_base = instance->time_sync_ts;
> +	vector[2].iov_len = sizeof(long long) * instance->time_sync_count;
> +	vector[2].iov_base = instance->time_sync_ofssets;
> +	tracecmd_add_option_v(handle, TRACECMD_OPTION_TIME_SHIFT, vector, 3);
> +	tracecmd_append_options(handle);
> +	tracecmd_output_close(handle);
> +#ifdef TSYNC_DEBUG
> +	if (instance->time_sync_count > 1)
> +		printf("\n\rDetected %lld ns ts offset drift in %lld ns for guest %s\n\r",
> +			instance->time_sync_ofssets[instance->time_sync_count-1] -
> +			instance->time_sync_ofssets[0],
> +			instance->time_sync_ts[instance->time_sync_count-1]-
> +			instance->time_sync_ts[0], instance->name);
> +#endif
> +}
> +
>  /*
>   * This function contains common code for the following commands:
>   * record, start, stream, profile.
> @@ -5738,6 +5790,22 @@ static void record_trace(int argc, char **argv,
>  	if (!latency)
>  		wait_threads();
>  
> +	if (ctx->instance && is_agent(ctx->instance)) {
> +		sync_time_with_host_v3(ctx->instance);
> +		tracecmd_clock_context_free(ctx->instance->clock_sync);
> +		ctx->instance->clock_sync = NULL;
> +	} else {
> +		for_all_instances(instance) {
> +			if (is_guest(instance)) {
> +				sync_time_with_guest_v3(instance);
> +				write_guest_time_shift(instance);
> +				tracecmd_clock_context_free(instance->clock_sync);
> +				instance->clock_sync = NULL;
> +			}
> +		}
> +	}
> +	close_guests_handle();
> +
>  	if (IS_RECORD(ctx)) {
>  		record_data(ctx);
>  		delete_thread_data();
> diff --git a/tracecmd/trace-timesync.c b/tracecmd/trace-timesync.c
> new file mode 100644
> index 0000000..f8366ed
> --- /dev/null
> +++ b/tracecmd/trace-timesync.c
> @@ -0,0 +1,622 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tstoyanov@vmware.com>
> + *
> + */
> +
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <arpa/inet.h>
> +#include <linux/vm_sockets.h>
> +#include "trace-local.h"
> +
> +static int clock_sync_x86_host_init(struct tracecmd_clock_sync *clock_context);
> +static int clock_sync_x86_host_free(struct tracecmd_clock_sync *clock_context);
> +static int clock_sync_x86_host_find_events(struct tracecmd_clock_sync *clock,
> +				    int cpu, struct tracecmd_event_hit *event);
> +static int clock_sync_x86_guest_init(struct tracecmd_clock_sync *clock_context);
> +static int clock_sync_x86_guest_free(struct tracecmd_clock_sync *clock_context);
> +static int clock_sync_x86_guest_find_events(struct tracecmd_clock_sync *clock,
> +					    int pid,
> +					    struct tracecmd_event_hit *event);
> +
> +struct tracecmd_event_descr {
> +	char			*system;
> +	char			*name;
> +};
> +
> +struct {
> +	char *systems[3];
> +	struct tracecmd_ftrace_param ftrace_params[5];
> +	struct tracecmd_event_descr events[3];
> +	int (*clock_sync_init)(struct tracecmd_clock_sync *clock_context);
> +	int (*clock_sync_free)(struct tracecmd_clock_sync *clock_context);
> +	int (*clock_sync_find_events)(struct tracecmd_clock_sync *clock_context,
> +				      int pid,
> +				      struct tracecmd_event_hit *event);
> +} static clock_sync[CLOCK_CONTEXT_MAX] = {
> +	{ /* CLOCK_KVM_X86_VSOCK_HOST */
> +	  .systems = {"vsock", "ftrace", NULL},
> +	  .ftrace_params = {
> +	  {"set_ftrace_filter", "vmx_read_l1_tsc_offset\nsvm_read_l1_tsc_offset", "\0"},
> +	  {"current_tracer", "function", "nop"},
> +	  {"events/vsock/virtio_transport_recv_pkt/enable", "1", "0"},
> +	  {"events/vsock/virtio_transport_recv_pkt/filter", NULL, "\0"},
> +	  {NULL, NULL, NULL} },
> +	  .events = {
> +	  {.system = "ftrace", .name = "function"},
> +	  {.system = "vsock", .name = "virtio_transport_recv_pkt"},
> +	  {.system = NULL, .name = NULL} },
> +	 clock_sync_x86_host_init,
> +	 clock_sync_x86_host_free,
> +	 clock_sync_x86_host_find_events,
> +	},
> +
> +	{ /* CLOCK_KVM_X86_VSOCK_GUEST */
> +	 .systems = { "vsock", "ftrace", NULL},
> +	 .ftrace_params = {
> +	  {"set_ftrace_filter", "vp_notify", "\0"},
> +	  {"current_tracer", "function", "nop"},
> +	  {"events/vsock/virtio_transport_alloc_pkt/enable", "1", "0"},
> +	  {"events/vsock/virtio_transport_alloc_pkt/filter", NULL, "\0"},
> +	  {NULL, NULL, NULL},
> +	  },
> +	  .events = {
> +	  {.system = "vsock", .name = "virtio_transport_alloc_pkt"},
> +	  {.system = "ftrace", .name = "function"},
> +	  {.system = NULL, .name = NULL}
> +	 },
> +	  clock_sync_x86_guest_init,
> +	  clock_sync_x86_guest_free,
> +	  clock_sync_x86_guest_find_events,
> +	}
> +};
> +
> +static int clock_sync_x86_host_init(struct tracecmd_clock_sync *clock_context)
> +{
> +	char vsock_filter[255];
> +
> +	snprintf(vsock_filter, 255,
> +		"src_cid==%u && src_port==%u && dst_cid==%u && dst_port==%u && len!=0",
> +		clock_context->remote_cid, clock_context->remote_port,
> +		clock_context->local_cid, clock_context->local_port);
> +
> +	clock_context->ftrace_params[3].set = strdup(vsock_filter);
> +	return 1;
> +}
> +
> +static int clock_sync_x86_host_free(struct tracecmd_clock_sync *clock_context)
> +{
> +	free(clock_context->ftrace_params[3].set);
> +	clock_context->ftrace_params[3].set = NULL;
> +	return 1;
> +}
> +
> +static int clock_sync_x86_guest_init(struct tracecmd_clock_sync *clock_context)
> +{
> +	char vsock_filter[255];
> +
> +	snprintf(vsock_filter, 255,
> +		"src_cid==%u && src_port==%u && dst_cid==%u && dst_port==%u && len!=0",
> +		clock_context->local_cid, clock_context->local_port,
> +		clock_context->remote_cid, clock_context->remote_port);
> +
> +	clock_context->ftrace_params[3].set = strdup(vsock_filter);
> +	return 1;
> +}
> +
> +static int clock_sync_x86_guest_free(struct tracecmd_clock_sync *clock_context)
> +{
> +	free(clock_context->ftrace_params[3].set);
> +	clock_context->ftrace_params[3].set = NULL;
> +	return 1;
> +}
> +
> +#define EVENTS_CHUNK	10
> +static int
> +get_events_in_page(struct tep_handle *tep, void *page,
> +		    int size, int cpu, struct tracecmd_event_hit **events,
> +		    int *events_count, int *events_size)
> +{
> +	struct tracecmd_event_hit *events_array = NULL;
> +	struct tep_record *last_record = NULL;
> +	struct tep_event *event = NULL;
> +	struct tep_record *record;
> +	int id, cnt = 0;
> +
> +	if (size <= 0)
> +		return 0;
> +
> +	if (*events == NULL) {
> +		*events = malloc(EVENTS_CHUNK*sizeof(struct tracecmd_event_hit));
> +		*events_size = EVENTS_CHUNK;
> +		*events_count = 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_data_event_from_type(tep, id);
> +		if (event) {
> +			if (*events_count >= *events_size) {
> +				events_array = realloc(*events,
> +					(*events_size + EVENTS_CHUNK)*
> +					sizeof(struct tracecmd_event_hit));
> +				if (events_array) {
> +					*events = events_array;
> +					(*events_size) += EVENTS_CHUNK;
> +				}

Reallocating in chunks only reduces the number of realloc() calls by constant. If this is performance critical, let's do exponential buffer growth so that we call realloc() only log(#events) times. Otherwise, let's just not bother and call realloc() each time which is also (empirically) linear but with higher constant factor.

> +			}
> +
> +			if (*events_count < *events_size) {
> +				(*events)[*events_count].ts = record->ts;
> +				(*events)[*events_count].cpu = cpu;
> +				(*events)[*events_count].id = id;
> +				(*events)[*events_count].pid = tep_data_pid(tep, record);
> +				(*events_count)++;
> +				cnt++;
> +			}
> +		}
> +		last_record = record;
> +	}
> +	free_record(last_record);
> +
> +	return cnt;
> +}
> +
> +static int sync_events_cmp(const void *a, const void *b)
> +{
> +	const struct tracecmd_event_hit *ea = (const struct tracecmd_event_hit *)a;
> +	const struct tracecmd_event_hit *eb = (const struct tracecmd_event_hit *)b;
> +
> +	if (ea->ts > eb->ts)
> +		return 1;
> +	if (ea->ts < eb->ts)
> +		return -1;
> +	return 0;
> +}
> +
> +static int find_sync_events(struct tep_handle *pevent,
> +			    struct tracecmd_event_hit *recorded,
> +			    int rsize, struct tracecmd_event_hit *events)
> +{
> +	int i = 0, j = 0;
> +
> +	while (i < rsize) {
> +		if (!events[j].ts && events[j].id == recorded[i].id &&
> +		    (events[j].pid < 0 || events[j].pid == recorded[i].pid)) {
> +			events[j].cpu = recorded[i].cpu;
> +			events[j].ts = recorded[i].ts;
> +			j++;
> +		} else if (j > 0 && events[j-1].id == recorded[i].id &&
> +			  (events[j-1].pid < 0 || events[j-1].pid == recorded[i].pid)) {
> +			events[j-1].cpu = recorded[i].cpu;
> +			events[j-1].ts = recorded[i].ts;
> +		}
> +		i++;
> +	}
> +	return j;
> +}
> +
> +//#define TSYNC_RBUFFER_DEBUG
> +static int find_raw_events(struct tep_handle *tep,
> +		    struct buffer_instance *instance,
> +		    struct tracecmd_event_hit *events)
> +{
> +	struct tracecmd_event_hit *events_array = NULL;
> +	int events_count = 0;
> +	int events_size = 0;
> +	struct dirent *dent;
> +	unsigned int p_size;
> +	int ts = 0;
> +	void *page;
> +	char *path;
> +	char *file;
> +	DIR *dir;
> +	int cpu;
> +	int len;
> +	int fd;
> +	int r;
> +
> +	p_size = getpagesize();
> +#ifdef TSYNC_RBUFFER_DEBUG
> +	file = get_instance_file(instance, "trace");
> +	if (!file)
> +		return ts;
> +	{
> +		char *buf = NULL;
> +		FILE *fp;
> +		size_t n;
> +		int r;
> +
> +		printf("Events:\n\r");
> +		fp = fopen(file, "r");
> +		while ((r = getline(&buf, &n, fp)) >= 0) {
> +
> +			if (buf[0] != '#')
> +				printf("%s", buf);
> +			free(buf);
> +			buf = NULL;
> +		}
> +		fclose(fp);
> +	}
> +	tracecmd_put_tracing_file(file);
> +#endif /* TSYNC_RBUFFER_DEBUG */
> +	path = get_instance_file(instance, "per_cpu");
> +	if (!path)
> +		return ts;
> +
> +	dir = opendir(path);
> +	if (!dir)
> +		goto out;
> +
> +	len = strlen(path);
> +	file = malloc(len + strlen("trace_pipe_raw") + 32);
> +	page = malloc(p_size);
> +	if (!file || !page)
> +		die("Failed to allocate time_stamp info");
> +
> +	while ((dent = readdir(dir))) {
> +
> +		const char *name = dent->d_name;
> +
> +		if (strncmp(name, "cpu", 3) != 0)
> +			continue;
> +		cpu = atoi(&name[3]);
> +		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,
> +						   &events_array, &events_count,
> +						   &events_size);
> +			}
> +		} while (r > 0);
> +		close(fd);
> +	}
> +	qsort(events_array, events_count, sizeof(*events_array), sync_events_cmp);
> +	r = find_sync_events(tep, events_array, events_count, events);
> +#ifdef TSYNC_RBUFFER_DEBUG
> +	len = 0;
> +	while (events[len].id) {
> +		printf("Found %d @ cpu %d: %lld pid %d\n\r",
> +			events[len].id,  events[len].cpu,
> +			events[len].ts, events[len].pid);
> +		len++;
> +	}
> +#endif
> +
> +	free(events_array);
> +	free(file);
> +	free(page);
> +	closedir(dir);
> +
> + out:
> +	tracecmd_put_tracing_file(path);
> +	return r;
> +}
> +
> +static int clock_sync_x86_host_find_events(struct tracecmd_clock_sync *clock,
> +					   int pid,
> +					   struct tracecmd_event_hit *event)
> +{
> +	int ret;
> +
> +	clock->events[0].pid = pid;
> +	ret = find_raw_events(clock->tep, clock->vinst, clock->events);
> +	event->ts = clock->events[0].ts;
> +	event->cpu = clock->events[0].cpu;
> +	return ret;
> +
> +}
> +
> +static int clock_sync_x86_guest_find_events(struct tracecmd_clock_sync *clock,
> +					    int pid,
> +					    struct tracecmd_event_hit *event)
> +{
> +	int ret;
> +
> +	ret = find_raw_events(clock->tep, clock->vinst, clock->events);
> +	event->ts = clock->events[1].ts;
> +	event->cpu = clock->events[0].cpu;
> +	return ret;
> +
> +}
> +
> +static void tracecmd_clock_sync_reset(struct tracecmd_clock_sync *clock_context)
> +{
> +	int i = 0;
> +
> +	while (clock_context->events[i].id) {
> +		clock_context->events[i].cpu = 0;
> +		clock_context->events[i].ts = 0;
> +		clock_context->events[i].pid = -1;
> +		i++;
> +	}
> +}
> +
> +int tracecmd_clock_find_event(struct tracecmd_clock_sync *clock, int pid,
> +			      struct tracecmd_event_hit *event)
> +{
> +	int ret = 0;
> +	int id;
> +
> +	if (clock == NULL ||
> +	    clock->clock_context_id >= CLOCK_CONTEXT_MAX)
> +		return 0;
> +
> +	id = clock->clock_context_id;
> +	if (clock_sync[id].clock_sync_find_events)
> +		ret = clock_sync[id].clock_sync_find_events(clock, pid, event);
> +
> +	tracecmd_clock_sync_reset(clock);
> +	return ret;
> +}
> +
> +static void clock_context_copy(struct tracecmd_clock_sync *clock_context,
> +			       struct tracecmd_ftrace_param *params,
> +			       struct tracecmd_event_descr *events)
> +{
> +	int i;
> +
> +	i = 0;
> +	while (params[i].file)
> +		i++;
> +	clock_context->ftrace_params = calloc(i+1, sizeof(struct tracecmd_ftrace_param));
> +	i = 0;
> +	while (params[i].file) {
> +		clock_context->ftrace_params[i].file = strdup(params[i].file);
> +		if (params[i].set)
> +			clock_context->ftrace_params[i].set = strdup(params[i].set);
> +		if (params[i].reset)
> +			clock_context->ftrace_params[i].reset = strdup(params[i].reset);
> +		i++;
> +	}
> +
> +	i = 0;
> +	while (events[i].name)
> +		i++;
> +	clock_context->events = calloc(i+1, sizeof(struct tracecmd_event_hit));
> +}
> +
> +void trace_instance_reset(struct buffer_instance *vinst)
> +{
> +	write_instance_file(vinst, "trace", "\0", NULL);
> +}
> +
> +static struct buffer_instance *
> +clock_synch_create_instance(const char *clock, unsigned int cid)
> +{
> +	struct buffer_instance *vinst;
> +	char inst_name[256];
> +
> +	snprintf(inst_name, 256, "clock_synch-%d", cid);
> +
> +	vinst = create_instance(strdup(inst_name));
> +	tracecmd_init_instance(vinst);
> +	vinst->cpu_count = tracecmd_local_cpu_count();
> +	tracecmd_make_instance(vinst);
> +	trace_instance_reset(vinst);
> +	if (clock)
> +		vinst->clock = strdup(clock);
> +	tracecmd_set_clock(vinst);
> +	return vinst;
> +}
> +
> +static struct tep_handle *clock_synch_get_tep(struct buffer_instance *instance,
> +					      char **systems)
> +{
> +	struct tep_handle *tep = NULL;
> +	char *path;
> +
> +	path = get_instance_dir(instance);
> +	tep = tracecmd_local_events_system(path, systems);
> +	tracecmd_put_tracing_file(path);
> +
> +	tep_set_file_bigendian(tep, tracecmd_host_bigendian());
> +	tep_set_host_bigendian(tep, tracecmd_host_bigendian());
> +
> +	return tep;
> +}
> +
> +static void 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);
> +
> +	if (lcid || lport) {
> +		memset(&addr, 0, sizeof(addr));
> +		if (getsockname(fd, (struct sockaddr *)&addr, &addr_len))
> +			return;
> +		if (addr.svm_family != AF_VSOCK)
> +			return;
> +		if (lport)
> +			*lport = addr.svm_port;
> +		if (lcid)
> +			*lcid = addr.svm_cid;
> +	}
> +
> +	if (rcid || rport) {
> +		memset(&addr, 0, sizeof(addr));
> +		addr_len = sizeof(addr);
> +		if (getpeername(fd, (struct sockaddr *)&addr, &addr_len))
> +			return;
> +		if (addr.svm_family != AF_VSOCK)
> +			return;
> +
> +		if (rport)
> +			*rport = addr.svm_port;
> +		if (rcid)
> +			*rcid = addr.svm_cid;
> +	}
> +}
> +
> +struct tracecmd_clock_sync*
> +tracecmd_clock_context_init(struct tracecmd_msg_handle *msg_handle,
> +			    const char *clock_str,
> +			    enum clock_sync_conext id)
> +{
> +	struct tracecmd_clock_sync *clock_context;
> +	struct tep_event *event;
> +	unsigned int i = 0;
> +
> +	switch (id) {
> +#ifdef VSOCK
> +	case CLOCK_KVM_X86_VSOCK_HOST:
> +	case CLOCK_KVM_X86_VSOCK_GUEST:
> +		break;
> +#endif
> +	default: /* not supported clock sync context */
> +		return NULL;
> +	}
> +
> +	if (id >= CLOCK_CONTEXT_MAX || NULL == msg_handle)
> +		return NULL;
> +	clock_context = calloc(1, sizeof(struct tracecmd_clock_sync));
> +	if (!clock_context)
> +		return NULL;
> +	clock_context->clock_context_id = id;
> +	clock_context_copy(clock_context,
> +			   clock_sync[id].ftrace_params, clock_sync[id].events);
> +
> +	get_vsocket_params(msg_handle->fd,
> +			   &(clock_context->local_cid),
> +			   &(clock_context->local_port),
> +			   &(clock_context->remote_cid),
> +			   &(clock_context->remote_port));
> +
> +	if (clock_sync[id].clock_sync_init)
> +		clock_sync[id].clock_sync_init(clock_context);
> +
> +	clock_context->vinst = clock_synch_create_instance(clock_str, clock_context->remote_cid);
> +	clock_context->tep = clock_synch_get_tep(clock_context->vinst,
> +						 clock_sync[id].systems);
> +	i = 0;
> +	while (clock_sync[id].events[i].name) {
> +		event = tep_find_event_by_name(clock_context->tep,
> +					       clock_sync[id].events[i].system,
> +					       clock_sync[id].events[i].name);
> +		if (!event)
> +			break;
> +		clock_context->events[i].id = event->id;
> +		i++;
> +	}
> +	return clock_context;
> +
> +}
> +
> +void tracecmd_clock_context_free(struct tracecmd_clock_sync *clock_context)
> +{
> +	int i;
> +
> +	if (clock_context == NULL ||
> +	    clock_context->clock_context_id >= CLOCK_CONTEXT_MAX)
> +		return;
> +	if (clock_sync[clock_context->clock_context_id].clock_sync_free)
> +		clock_sync[clock_context->clock_context_id].clock_sync_free(clock_context);
> +
> +	i = 0;
> +	while (clock_context->ftrace_params[i].file) {
> +		free(clock_context->ftrace_params[i].file);
> +		free(clock_context->ftrace_params[i].set);
> +		free(clock_context->ftrace_params[i].reset);
> +		i++;
> +	}
> +	free(clock_context->ftrace_params);
> +	free(clock_context->events);
> +	tracecmd_remove_instance(clock_context->vinst);
> +	/* todo: clean up the instance */
> +	tep_free(clock_context->tep);
> +	free(clock_context);
> +}
> +
> +void sync_time_with_host_v3(struct buffer_instance *instance)
> +{
> +	long long ts_local = 0;
> +	long long toffset = 0;
> +
> +	if (instance->clock_sync == NULL)
> +		instance->clock_sync = tracecmd_clock_context_init(instance->msg_handle,
> +					instance->clock, CLOCK_KVM_X86_VSOCK_GUEST);
> +
> +	tracecmd_msg_snd_time_sync(instance->msg_handle, instance->clock_sync,
> +				   &toffset, &ts_local);
> +}
> +
> +void sync_time_with_guest_v3(struct buffer_instance *instance)
> +{
> +	long long ts_local = 0;
> +	long long toffset = 0;

How about switching throughout `ts_local` and `toffset` to `guest_timestamp` and `guest_offset` respectively? I think it will make the code easier to read.

> +	long long *sync_array_ts;
> +	long long *sync_array_ofs;
> +
> +	if (instance->clock_sync == NULL)
> +		instance->clock_sync = tracecmd_clock_context_init(instance->msg_handle,
> +						top_instance.clock, CLOCK_KVM_X86_VSOCK_HOST);
> +
> +	tracecmd_msg_rcv_time_sync(instance->msg_handle,
> +				   instance->clock_sync, &toffset, &ts_local);
> +
> +	sync_array_ts = realloc(instance->time_sync_ts,
> +			    (instance->time_sync_count+1)*sizeof(long long));
> +	sync_array_ofs = realloc(instance->time_sync_ofssets,
> +			    (instance->time_sync_count+1)*sizeof(long long));
> +
> +	if (sync_array_ts && sync_array_ofs) {
> +		sync_array_ts[instance->time_sync_count] = ts_local;
> +		sync_array_ofs[instance->time_sync_count] = toffset;
> +		instance->time_sync_count++;
> +		instance->time_sync_ts = sync_array_ts;
> +		instance->time_sync_ofssets = sync_array_ofs;
> +
> +	} else {
> +		free(sync_array_ts);
> +		free(sync_array_ofs);
> +	}
> +}
> +
> +static void set_clock_synch_events(struct buffer_instance *instance,
> +				   struct tracecmd_ftrace_param *params,
> +				   bool enable)
> +{
> +	int i = 0;
> +
> +	if (!enable)
> +		write_tracing_on(instance, 0);
> +
> +	while (params[i].file) {
> +		if (enable && params[i].set) {
> +			write_instance_file(instance, params[i].file,
> +					params[i].set, NULL);
> +		}
> +		if (!enable && params[i].reset)
> +			write_instance_file(instance, params[i].file,
> +					params[i].reset, NULL);
> +		i++;
> +	}
> +
> +	if (enable)
> +		write_tracing_on(instance, 1);
> +}
> +
> +void tracecmd_clock_synch_enable(struct tracecmd_clock_sync *clock_context)
> +{
> +	set_clock_synch_events(clock_context->vinst,
> +			       clock_context->ftrace_params, true);
> +}
> +
> +void tracecmd_clock_synch_disable(struct tracecmd_clock_sync *clock_context)
> +{
> +	set_clock_synch_events(clock_context->vinst,
> +			       clock_context->ftrace_params, false);
> +}
> -- 
> 2.20.1
> 

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

end of thread, other threads:[~2019-03-11 11:46 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-08 13:44 [PATCH v5 0/9] [POC] trace-cmd: Timetamps sync between host and guest machines, relying on vsock events Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 1/9] trace-cmd: Implemented new lib API: tracecmd_local_events_system() Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 2/9] trace-cmd: Added support for negative time offsets in trace.dat file Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 3/9] trace-cmd: Fix tracecmd_read_page_record() to read more than one event Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 4/9] trace-cmd: Added implementation of htonll() and ntohll() Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 5/9] trace-cmd: Refactored few functions in trace-record.c Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 6/9] trace-cmd: Find and store pids of tasks, which run virtual CPUs of given VM Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 7/9] trace-cmd: Implemented new API tracecmd_add_option_v() Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 8/9] trace-cmd: Implemented new option in trace.dat file: TRACECMD_OPTION_TIME_SHIFT Tzvetomir Stoyanov
2019-03-08 13:44 ` [PATCH v5 9/9] trace-cmd [POC]: Implemented timestamps synch PTP-like algorithm, using vsock events Tzvetomir Stoyanov
2019-03-11 11:46   ` Slavomir Kaslev

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.