Linux-Trace-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [RFC v2] Simple tool for VMEnters/VMExits matching and trace validation
@ 2021-05-01  6:07 Stefano De Venuto
  2021-05-03  6:54 ` Yordan Karadzhov
  0 siblings, 1 reply; 2+ messages in thread
From: Stefano De Venuto @ 2021-05-01  6:07 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, dfaggioli, Stefano De Venuto

Add a tool in examples/ that scans a merged host + guest trace and
search for host events that are inside a vmentry/vmexit block (and
vice-versa for guest events ouside the block) and report the found
ones.

It can be useful as a starting point for identifying issues of for
checking the effectiveness of host/guest traces synchronization, or
even for improving the placing of the tracepoints in the kernel.

v2 changes:
 - Addressed Yordan's comments.
 - Added a way to not mark as invalid the initial guests event.
 - Used kshark_read_event_field_int() for the vCPU field recovering.

Signed-off-by: Stefano De Venuto <stefano.devenuto99@gmail.com>
---
 examples/CMakeLists.txt |   4 +
 examples/checker.c      | 224 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 228 insertions(+)
 create mode 100644 examples/checker.c

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index c2f4c01..91badb8 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -12,6 +12,10 @@ message(STATUS "multibufferload")
 add_executable(mbload          multibufferload.c)
 target_link_libraries(mbload   kshark)
 
+message(STATUS "checker")
+add_executable(checker          checker.c)
+target_link_libraries(checker   kshark)
+
 message(STATUS "datahisto")
 add_executable(dhisto          datahisto.c)
 target_link_libraries(dhisto   kshark)
diff --git a/examples/checker.c b/examples/checker.c
new file mode 100644
index 0000000..425de58
--- /dev/null
+++ b/examples/checker.c
@@ -0,0 +1,224 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libkshark.h"
+#include "libkshark-tepdata.h"
+
+#define KVM_ENTRY "kvm/kvm_entry"
+#define KVM_EXIT "kvm/kvm_exit"
+
+struct custom_stream
+{
+	struct kshark_data_stream* original_stream;
+	int* cpus;
+};
+
+/**
+ * Checks if a stream_id represents a guest.
+ * If so, @host contains the corresponding host stream_id
+ */
+int is_guest(int stream_id,
+	     struct kshark_host_guest_map* mapping,
+	     int n_mapping, int* host)
+{
+	for (int i = 0; i < n_mapping; i++) {
+		if (mapping[i].guest_id == stream_id) {
+			*host = mapping[i].host_id;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Recover guest stream_id from host VMentry/VMexit event.
+ * In case of success, @guest_id will contain the guest stream_id.
+ */
+int guest_id_from_host_entry_exit(struct kshark_host_guest_map* mapping,
+				  int n_mapping, int* guest_id,
+				  struct kshark_entry* entry)
+{
+	struct kshark_data_stream* stream;
+	int pid;
+
+	stream = kshark_get_stream_from_entry(entry);
+	pid = kshark_get_pid(entry);
+
+	for (int i = 0; i < n_mapping; i++)
+		if (mapping[i].host_id == stream->stream_id)
+			for (int j = 0; j < mapping[i].vcpu_count; j++)
+				if (mapping[i].cpu_pid[j] == pid) {
+					*guest_id = mapping[i].guest_id;
+					return 1;
+				}
+
+	return 0;
+}
+
+void print_entry(struct kshark_entry* entry)
+{
+	struct kshark_data_stream* stream;
+	char* event_name;
+
+	stream = kshark_get_stream_from_entry(entry);
+	event_name = kshark_get_event_name(entry);
+
+	printf("      %d: %s-%d, %" PRId64 " [%03d]:%s\t%s\n",
+		stream->stream_id,
+		kshark_get_task(entry),
+		kshark_get_pid(entry),
+		entry->ts,
+		entry->cpu,
+		event_name,
+		kshark_get_info(entry));
+}
+
+void free_data(struct kshark_context *kshark_ctx,
+	       struct custom_stream** custom_streams,
+	       struct kshark_entry** entries, int n_entries,
+	       struct kshark_host_guest_map* host_guest_mapping,
+	       int n_guest)
+{
+	struct custom_stream* custom_stream;
+
+	for (int i = 0; i < kshark_ctx->n_streams; i++) {
+		custom_stream = custom_streams[i];
+
+		free(custom_stream->cpus);
+		free(custom_stream);
+	}
+	free(custom_streams);
+
+	for (int i = 0; i < n_entries; i++)
+		free(entries[i]);
+	free(entries);
+
+	kshark_tracecmd_free_hostguest_map(host_guest_mapping, n_guest);
+}
+
+int main(int argc, char **argv)
+{
+	struct kshark_host_guest_map* host_guest_mapping;
+	struct custom_stream** custom_streams;
+	struct custom_stream* custom_stream;
+	struct custom_stream* guest_stream;
+	struct custom_stream* host_stream;
+	struct kshark_data_stream* stream;
+	struct kshark_context* kshark_ctx;
+	struct kshark_entry** entries;
+	struct kshark_entry* current;
+	ssize_t n_entries;
+	char* event_name;
+	int64_t vcpu;
+	int guest_id;
+	int n_guest;
+	int host;
+	int v_i;
+	int sd;
+
+	kshark_ctx = NULL;
+	if (!kshark_instance(&kshark_ctx))
+		return 1;
+
+	custom_streams = malloc(sizeof(*custom_streams) * (argc-1));
+
+	for (int i = 1; i < argc; i++) {
+		sd = kshark_open(kshark_ctx, argv[i]);
+		if (sd < 0) {
+			kshark_free(kshark_ctx);
+			return 1;
+		}
+
+		kshark_tep_init_all_buffers(kshark_ctx, sd);
+
+		/**
+		 * Creating custom streams in order to keep track if a
+		 * pCPU is executing code of a vCPU and, if so, which vCPU.
+		 */
+		custom_stream = malloc(sizeof(*custom_stream));
+		custom_stream->original_stream = kshark_get_data_stream(kshark_ctx, sd);
+		custom_stream->cpus = malloc(custom_stream->original_stream->n_cpus * sizeof(int));
+		memset(custom_stream->cpus, -1, custom_stream->original_stream->n_cpus * sizeof(int));
+
+		custom_streams[i-1] = custom_stream;
+	}
+
+	host_guest_mapping = NULL;
+	n_guest = kshark_tracecmd_get_hostguest_mapping(&host_guest_mapping);
+	if (n_guest < 0) {
+		printf("Failed mapping: %d\n", n_guest);
+		return 1;
+	}
+
+	entries = NULL;
+	n_entries = kshark_load_all_entries(kshark_ctx, &entries);
+
+	for (int i = 0; i < n_entries; ++i) {
+		current = entries[i];
+
+		stream = kshark_get_stream_from_entry(current);
+		event_name = kshark_get_event_name(current);
+
+		custom_stream = custom_streams[stream->stream_id];
+
+		if (!strcmp(event_name, KVM_ENTRY) || !strcmp(event_name, KVM_EXIT)) {
+			if (kshark_read_event_field_int(current, "vcpu_id", &vcpu)) {
+				printf("Error on recovering the vCPU field\n");
+				return 1;
+			}
+
+			if (!guest_id_from_host_entry_exit(host_guest_mapping, n_guest, &guest_id, current)) {
+				printf("Error on recovering guest stream ID\n");
+				return 1;
+			}
+
+			/**
+			 * Mark the vCPU upcoming events as checkable.
+			 * Workaround implemented in order to not mark as invalid initial guests events.
+			 * Done in this way since we can't know if we'll find a kvm_exit (consistent state)
+			 * or a kvm_entry.
+			 */
+			guest_stream = custom_streams[guest_id];
+			guest_stream->cpus[vcpu] = 1;
+
+			if (!strcmp(event_name, KVM_ENTRY))
+				custom_stream->cpus[current->cpu] = vcpu;
+			else
+				custom_stream->cpus[current->cpu] = -1;
+
+		} else {
+
+			/**
+			 * If the event comes from a guest, recover the pCPU where the event was executed
+			 * and check if it's NOT OUTSIDE a kvm_entry/kvm_exit block.
+			 */
+			if (is_guest(stream->stream_id, host_guest_mapping, n_guest, &host)) {
+				host_stream = custom_streams[host];
+
+				for (v_i = 0; v_i < host_stream->original_stream->n_cpus; v_i++) {
+					if (current->cpu == host_stream->cpus[v_i])
+					break;
+				}
+
+				if (v_i == host_stream->original_stream->n_cpus && custom_stream->cpus[current->cpu] != -1)
+					printf("%d G out:\t", i);
+
+			/**
+			 * If the event comes from a host, recover the CPU that executed the event
+			 * and check if it's NOT INSIDE a kvm_entry/kvm_exit block.
+			 */
+			} else {
+				if (custom_stream->cpus[current->cpu] != -1)
+					printf("%d H in:\t", i);
+			}
+		}
+
+		print_entry(entries[i]);
+	}
+
+	free_data(kshark_ctx, custom_streams, entries, n_entries, host_guest_mapping, n_guest);
+
+	kshark_free(kshark_ctx);
+}
-- 
2.31.1


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

* Re: [RFC v2] Simple tool for VMEnters/VMExits matching and trace validation
  2021-05-01  6:07 [RFC v2] Simple tool for VMEnters/VMExits matching and trace validation Stefano De Venuto
@ 2021-05-03  6:54 ` Yordan Karadzhov
  0 siblings, 0 replies; 2+ messages in thread
From: Yordan Karadzhov @ 2021-05-03  6:54 UTC (permalink / raw)
  To: Stefano De Venuto, rostedt; +Cc: linux-trace-devel, dfaggioli

Ciao Stefano,

I am on holiday this week. I will review the code next week when I am 
back to work.

Cheers,
Yordan

On 1.05.21 г. 9:07, Stefano De Venuto wrote:
> Add a tool in examples/ that scans a merged host + guest trace and
> search for host events that are inside a vmentry/vmexit block (and
> vice-versa for guest events ouside the block) and report the found
> ones.
> 
> It can be useful as a starting point for identifying issues of for
> checking the effectiveness of host/guest traces synchronization, or
> even for improving the placing of the tracepoints in the kernel.
> 
> v2 changes:
>   - Addressed Yordan's comments.
>   - Added a way to not mark as invalid the initial guests event.
>   - Used kshark_read_event_field_int() for the vCPU field recovering.
> 
> Signed-off-by: Stefano De Venuto <stefano.devenuto99@gmail.com>
> ---
>   examples/CMakeLists.txt |   4 +
>   examples/checker.c      | 224 ++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 228 insertions(+)
>   create mode 100644 examples/checker.c
> 
> diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
> index c2f4c01..91badb8 100644
> --- a/examples/CMakeLists.txt
> +++ b/examples/CMakeLists.txt
> @@ -12,6 +12,10 @@ message(STATUS "multibufferload")
>   add_executable(mbload          multibufferload.c)
>   target_link_libraries(mbload   kshark)
>   
> +message(STATUS "checker")
> +add_executable(checker          checker.c)
> +target_link_libraries(checker   kshark)
> +
>   message(STATUS "datahisto")
>   add_executable(dhisto          datahisto.c)
>   target_link_libraries(dhisto   kshark)
> diff --git a/examples/checker.c b/examples/checker.c
> new file mode 100644
> index 0000000..425de58
> --- /dev/null
> +++ b/examples/checker.c
> @@ -0,0 +1,224 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "libkshark.h"
> +#include "libkshark-tepdata.h"
> +
> +#define KVM_ENTRY "kvm/kvm_entry"
> +#define KVM_EXIT "kvm/kvm_exit"
> +
> +struct custom_stream
> +{
> +	struct kshark_data_stream* original_stream;
> +	int* cpus;
> +};
> +
> +/**
> + * Checks if a stream_id represents a guest.
> + * If so, @host contains the corresponding host stream_id
> + */
> +int is_guest(int stream_id,
> +	     struct kshark_host_guest_map* mapping,
> +	     int n_mapping, int* host)
> +{
> +	for (int i = 0; i < n_mapping; i++) {
> +		if (mapping[i].guest_id == stream_id) {
> +			*host = mapping[i].host_id;
> +			return 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * Recover guest stream_id from host VMentry/VMexit event.
> + * In case of success, @guest_id will contain the guest stream_id.
> + */
> +int guest_id_from_host_entry_exit(struct kshark_host_guest_map* mapping,
> +				  int n_mapping, int* guest_id,
> +				  struct kshark_entry* entry)
> +{
> +	struct kshark_data_stream* stream;
> +	int pid;
> +
> +	stream = kshark_get_stream_from_entry(entry);
> +	pid = kshark_get_pid(entry);
> +
> +	for (int i = 0; i < n_mapping; i++)
> +		if (mapping[i].host_id == stream->stream_id)
> +			for (int j = 0; j < mapping[i].vcpu_count; j++)
> +				if (mapping[i].cpu_pid[j] == pid) {
> +					*guest_id = mapping[i].guest_id;
> +					return 1;
> +				}
> +
> +	return 0;
> +}
> +
> +void print_entry(struct kshark_entry* entry)
> +{
> +	struct kshark_data_stream* stream;
> +	char* event_name;
> +
> +	stream = kshark_get_stream_from_entry(entry);
> +	event_name = kshark_get_event_name(entry);
> +
> +	printf("      %d: %s-%d, %" PRId64 " [%03d]:%s\t%s\n",
> +		stream->stream_id,
> +		kshark_get_task(entry),
> +		kshark_get_pid(entry),
> +		entry->ts,
> +		entry->cpu,
> +		event_name,
> +		kshark_get_info(entry));
> +}
> +
> +void free_data(struct kshark_context *kshark_ctx,
> +	       struct custom_stream** custom_streams,
> +	       struct kshark_entry** entries, int n_entries,
> +	       struct kshark_host_guest_map* host_guest_mapping,
> +	       int n_guest)
> +{
> +	struct custom_stream* custom_stream;
> +
> +	for (int i = 0; i < kshark_ctx->n_streams; i++) {
> +		custom_stream = custom_streams[i];
> +
> +		free(custom_stream->cpus);
> +		free(custom_stream);
> +	}
> +	free(custom_streams);
> +
> +	for (int i = 0; i < n_entries; i++)
> +		free(entries[i]);
> +	free(entries);
> +
> +	kshark_tracecmd_free_hostguest_map(host_guest_mapping, n_guest);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	struct kshark_host_guest_map* host_guest_mapping;
> +	struct custom_stream** custom_streams;
> +	struct custom_stream* custom_stream;
> +	struct custom_stream* guest_stream;
> +	struct custom_stream* host_stream;
> +	struct kshark_data_stream* stream;
> +	struct kshark_context* kshark_ctx;
> +	struct kshark_entry** entries;
> +	struct kshark_entry* current;
> +	ssize_t n_entries;
> +	char* event_name;
> +	int64_t vcpu;
> +	int guest_id;
> +	int n_guest;
> +	int host;
> +	int v_i;
> +	int sd;
> +
> +	kshark_ctx = NULL;
> +	if (!kshark_instance(&kshark_ctx))
> +		return 1;
> +
> +	custom_streams = malloc(sizeof(*custom_streams) * (argc-1));
> +
> +	for (int i = 1; i < argc; i++) {
> +		sd = kshark_open(kshark_ctx, argv[i]);
> +		if (sd < 0) {
> +			kshark_free(kshark_ctx);
> +			return 1;
> +		}
> +
> +		kshark_tep_init_all_buffers(kshark_ctx, sd);
> +
> +		/**
> +		 * Creating custom streams in order to keep track if a
> +		 * pCPU is executing code of a vCPU and, if so, which vCPU.
> +		 */
> +		custom_stream = malloc(sizeof(*custom_stream));
> +		custom_stream->original_stream = kshark_get_data_stream(kshark_ctx, sd);
> +		custom_stream->cpus = malloc(custom_stream->original_stream->n_cpus * sizeof(int));
> +		memset(custom_stream->cpus, -1, custom_stream->original_stream->n_cpus * sizeof(int));
> +
> +		custom_streams[i-1] = custom_stream;
> +	}
> +
> +	host_guest_mapping = NULL;
> +	n_guest = kshark_tracecmd_get_hostguest_mapping(&host_guest_mapping);
> +	if (n_guest < 0) {
> +		printf("Failed mapping: %d\n", n_guest);
> +		return 1;
> +	}
> +
> +	entries = NULL;
> +	n_entries = kshark_load_all_entries(kshark_ctx, &entries);
> +
> +	for (int i = 0; i < n_entries; ++i) {
> +		current = entries[i];
> +
> +		stream = kshark_get_stream_from_entry(current);
> +		event_name = kshark_get_event_name(current);
> +
> +		custom_stream = custom_streams[stream->stream_id];
> +
> +		if (!strcmp(event_name, KVM_ENTRY) || !strcmp(event_name, KVM_EXIT)) {
> +			if (kshark_read_event_field_int(current, "vcpu_id", &vcpu)) {
> +				printf("Error on recovering the vCPU field\n");
> +				return 1;
> +			}
> +
> +			if (!guest_id_from_host_entry_exit(host_guest_mapping, n_guest, &guest_id, current)) {
> +				printf("Error on recovering guest stream ID\n");
> +				return 1;
> +			}
> +
> +			/**
> +			 * Mark the vCPU upcoming events as checkable.
> +			 * Workaround implemented in order to not mark as invalid initial guests events.
> +			 * Done in this way since we can't know if we'll find a kvm_exit (consistent state)
> +			 * or a kvm_entry.
> +			 */
> +			guest_stream = custom_streams[guest_id];
> +			guest_stream->cpus[vcpu] = 1;
> +
> +			if (!strcmp(event_name, KVM_ENTRY))
> +				custom_stream->cpus[current->cpu] = vcpu;
> +			else
> +				custom_stream->cpus[current->cpu] = -1;
> +
> +		} else {
> +
> +			/**
> +			 * If the event comes from a guest, recover the pCPU where the event was executed
> +			 * and check if it's NOT OUTSIDE a kvm_entry/kvm_exit block.
> +			 */
> +			if (is_guest(stream->stream_id, host_guest_mapping, n_guest, &host)) {
> +				host_stream = custom_streams[host];
> +
> +				for (v_i = 0; v_i < host_stream->original_stream->n_cpus; v_i++) {
> +					if (current->cpu == host_stream->cpus[v_i])
> +					break;
> +				}
> +
> +				if (v_i == host_stream->original_stream->n_cpus && custom_stream->cpus[current->cpu] != -1)
> +					printf("%d G out:\t", i);
> +
> +			/**
> +			 * If the event comes from a host, recover the CPU that executed the event
> +			 * and check if it's NOT INSIDE a kvm_entry/kvm_exit block.
> +			 */
> +			} else {
> +				if (custom_stream->cpus[current->cpu] != -1)
> +					printf("%d H in:\t", i);
> +			}
> +		}
> +
> +		print_entry(entries[i]);
> +	}
> +
> +	free_data(kshark_ctx, custom_streams, entries, n_entries, host_guest_mapping, n_guest);
> +
> +	kshark_free(kshark_ctx);
> +}
> 

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

end of thread, back to index

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-01  6:07 [RFC v2] Simple tool for VMEnters/VMExits matching and trace validation Stefano De Venuto
2021-05-03  6:54 ` Yordan Karadzhov

Linux-Trace-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-trace-devel/0 linux-trace-devel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-trace-devel linux-trace-devel/ https://lore.kernel.org/linux-trace-devel \
		linux-trace-devel@vger.kernel.org
	public-inbox-index linux-trace-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-trace-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git