linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH 1/8] perf db-export: Add calls parent_id
       [not found] ` <20190228130031.23064-2-adrian.hunter@intel.com>
@ 2019-03-01 17:52   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:52 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:24PM +0200, Adrian Hunter escreveu:
> The call_path can be used to find the parent symbol for a call but not the
> exact parent call. To do that add parent_id to the call_return export. This
> enables the creation of a call tree from the exported data.

Thanks, applied.

- Arnaldo
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  tools/perf/util/db-export.c                      | 15 ++++++++++-----
>  tools/perf/util/db-export.h                      |  3 ++-
>  .../util/scripting-engines/trace-event-python.c  |  8 +++++---
>  tools/perf/util/thread-stack.c                   | 16 ++++++++++++++--
>  tools/perf/util/thread-stack.h                   |  6 ++++--
>  5 files changed, 35 insertions(+), 13 deletions(-)
> 
> diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
> index de9b4769d06c..d7315a00c731 100644
> --- a/tools/perf/util/db-export.c
> +++ b/tools/perf/util/db-export.c
> @@ -510,18 +510,23 @@ int db_export__call_path(struct db_export *dbe, struct call_path *cp)
>  	return 0;
>  }
>  
> -int db_export__call_return(struct db_export *dbe, struct call_return *cr)
> +int db_export__call_return(struct db_export *dbe, struct call_return *cr,
> +			   u64 *parent_db_id)
>  {
>  	int err;
>  
> -	if (cr->db_id)
> -		return 0;
> -
>  	err = db_export__call_path(dbe, cr->cp);
>  	if (err)
>  		return err;
>  
> -	cr->db_id = ++dbe->call_return_last_db_id;
> +	if (!cr->db_id)
> +		cr->db_id = ++dbe->call_return_last_db_id;
> +
> +	if (parent_db_id) {
> +		if (!*parent_db_id)
> +			*parent_db_id = ++dbe->call_return_last_db_id;
> +		cr->parent_db_id = *parent_db_id;
> +	}
>  
>  	if (dbe->export_call_return)
>  		return dbe->export_call_return(dbe, cr);
> diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
> index 67bc6b8ad2d6..4e2424c89df9 100644
> --- a/tools/perf/util/db-export.h
> +++ b/tools/perf/util/db-export.h
> @@ -104,6 +104,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
>  int db_export__branch_types(struct db_export *dbe);
>  
>  int db_export__call_path(struct db_export *dbe, struct call_path *cp);
> -int db_export__call_return(struct db_export *dbe, struct call_return *cr);
> +int db_export__call_return(struct db_export *dbe, struct call_return *cr,
> +			   u64 *parent_db_id);
>  
>  #endif
> diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
> index 0e17db41b49b..09604c6508f0 100644
> --- a/tools/perf/util/scripting-engines/trace-event-python.c
> +++ b/tools/perf/util/scripting-engines/trace-event-python.c
> @@ -1173,7 +1173,7 @@ static int python_export_call_return(struct db_export *dbe,
>  	u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
>  	PyObject *t;
>  
> -	t = tuple_new(11);
> +	t = tuple_new(12);
>  
>  	tuple_set_u64(t, 0, cr->db_id);
>  	tuple_set_u64(t, 1, cr->thread->db_id);
> @@ -1186,6 +1186,7 @@ static int python_export_call_return(struct db_export *dbe,
>  	tuple_set_u64(t, 8, cr->return_ref);
>  	tuple_set_u64(t, 9, cr->cp->parent->db_id);
>  	tuple_set_s32(t, 10, cr->flags);
> +	tuple_set_u64(t, 11, cr->parent_db_id);
>  
>  	call_object(tables->call_return_handler, t, "call_return_table");
>  
> @@ -1194,11 +1195,12 @@ static int python_export_call_return(struct db_export *dbe,
>  	return 0;
>  }
>  
> -static int python_process_call_return(struct call_return *cr, void *data)
> +static int python_process_call_return(struct call_return *cr, u64 *parent_db_id,
> +				      void *data)
>  {
>  	struct db_export *dbe = data;
>  
> -	return db_export__call_return(dbe, cr);
> +	return db_export__call_return(dbe, cr, parent_db_id);
>  }
>  
>  static void python_process_general_event(struct perf_sample *sample,
> diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
> index a8b45168513c..41942c2aaa18 100644
> --- a/tools/perf/util/thread-stack.c
> +++ b/tools/perf/util/thread-stack.c
> @@ -49,6 +49,7 @@ enum retpoline_state_t {
>   * @timestamp: timestamp (if known)
>   * @ref: external reference (e.g. db_id of sample)
>   * @branch_count: the branch count when the entry was created
> + * @db_id: id used for db-export
>   * @cp: call path
>   * @no_call: a 'call' was not seen
>   * @trace_end: a 'call' but trace ended
> @@ -59,6 +60,7 @@ struct thread_stack_entry {
>  	u64 timestamp;
>  	u64 ref;
>  	u64 branch_count;
> +	u64 db_id;
>  	struct call_path *cp;
>  	bool no_call;
>  	bool trace_end;
> @@ -280,12 +282,14 @@ static int thread_stack__call_return(struct thread *thread,
>  		.comm = ts->comm,
>  		.db_id = 0,
>  	};
> +	u64 *parent_db_id;
>  
>  	tse = &ts->stack[idx];
>  	cr.cp = tse->cp;
>  	cr.call_time = tse->timestamp;
>  	cr.return_time = timestamp;
>  	cr.branch_count = ts->branch_count - tse->branch_count;
> +	cr.db_id = tse->db_id;
>  	cr.call_ref = tse->ref;
>  	cr.return_ref = ref;
>  	if (tse->no_call)
> @@ -295,7 +299,14 @@ static int thread_stack__call_return(struct thread *thread,
>  	if (tse->non_call)
>  		cr.flags |= CALL_RETURN_NON_CALL;
>  
> -	return crp->process(&cr, crp->data);
> +	/*
> +	 * The parent db_id must be assigned before exporting the child. Note
> +	 * it is not possible to export the parent first because its information
> +	 * is not yet complete because its 'return' has not yet been processed.
> +	 */
> +	parent_db_id = idx ? &(tse - 1)->db_id : NULL;
> +
> +	return crp->process(&cr, parent_db_id, crp->data);
>  }
>  
>  static int __thread_stack__flush(struct thread *thread, struct thread_stack *ts)
> @@ -484,7 +495,7 @@ void thread_stack__sample(struct thread *thread, int cpu,
>  }
>  
>  struct call_return_processor *
> -call_return_processor__new(int (*process)(struct call_return *cr, void *data),
> +call_return_processor__new(int (*process)(struct call_return *cr, u64 *parent_db_id, void *data),
>  			   void *data)
>  {
>  	struct call_return_processor *crp;
> @@ -537,6 +548,7 @@ static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
>  	tse->no_call = no_call;
>  	tse->trace_end = trace_end;
>  	tse->non_call = false;
> +	tse->db_id = 0;
>  
>  	return 0;
>  }
> diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
> index b7c04e19ad41..9c45f947f5a9 100644
> --- a/tools/perf/util/thread-stack.h
> +++ b/tools/perf/util/thread-stack.h
> @@ -55,6 +55,7 @@ enum {
>   * @call_ref: external reference to 'call' sample (e.g. db_id)
>   * @return_ref:  external reference to 'return' sample (e.g. db_id)
>   * @db_id: id used for db-export
> + * @parent_db_id: id of parent call used for db-export
>   * @flags: Call/Return flags
>   */
>  struct call_return {
> @@ -67,6 +68,7 @@ struct call_return {
>  	u64 call_ref;
>  	u64 return_ref;
>  	u64 db_id;
> +	u64 parent_db_id;
>  	u32 flags;
>  };
>  
> @@ -79,7 +81,7 @@ struct call_return {
>   */
>  struct call_return_processor {
>  	struct call_path_root *cpr;
> -	int (*process)(struct call_return *cr, void *data);
> +	int (*process)(struct call_return *cr, u64 *parent_db_id, void *data);
>  	void *data;
>  };
>  
> @@ -93,7 +95,7 @@ void thread_stack__free(struct thread *thread);
>  size_t thread_stack__depth(struct thread *thread, int cpu);
>  
>  struct call_return_processor *
> -call_return_processor__new(int (*process)(struct call_return *cr, void *data),
> +call_return_processor__new(int (*process)(struct call_return *cr, u64 *parent_db_id, void *data),
>  			   void *data);
>  void call_return_processor__free(struct call_return_processor *crp);
>  int thread_stack__process(struct thread *thread, struct comm *comm,
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 2/8] perf scripts python: export-to-sqlite.py: Export calls parent_id
       [not found] ` <20190228130031.23064-3-adrian.hunter@intel.com>
@ 2019-03-01 17:52   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:52 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:25PM +0200, Adrian Hunter escreveu:
> Export to the 'calls' table the newly created 'parent_id'.
> 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>

Thanks, applied.

- Arnaldo

> ---
>  tools/perf/scripts/python/export-to-sqlite.py | 12 ++++++++----
>  1 file changed, 8 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py
> index ed237f2ed03f..eb63e6c7107f 100644
> --- a/tools/perf/scripts/python/export-to-sqlite.py
> +++ b/tools/perf/scripts/python/export-to-sqlite.py
> @@ -222,7 +222,8 @@ if perf_db_export_calls:
>  		'call_id	bigint,'
>  		'return_id	bigint,'
>  		'parent_call_path_id	bigint,'
> -		'flags		integer)')
> +		'flags		integer,'
> +		'parent_id	bigint)')
>  
>  # printf was added to sqlite in version 3.8.3
>  sqlite_has_printf = False
> @@ -321,7 +322,8 @@ if perf_db_export_calls:
>  			'call_id,'
>  			'return_id,'
>  			'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE flags END AS flags,'
> -			'parent_call_path_id'
> +			'parent_call_path_id,'
> +			'parent_id'
>  		' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
>  
>  do_query(query, 'CREATE VIEW samples_view AS '
> @@ -373,7 +375,7 @@ if perf_db_export_calls or perf_db_export_callchains:
>  	call_path_query.prepare("INSERT INTO call_paths VALUES (?, ?, ?, ?)")
>  if perf_db_export_calls:
>  	call_query = QSqlQuery(db)
> -	call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
> +	call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
>  
>  def trace_begin():
>  	print datetime.datetime.today(), "Writing records..."
> @@ -388,6 +390,7 @@ def trace_begin():
>  	sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>  	if perf_db_export_calls or perf_db_export_callchains:
>  		call_path_table(0, 0, 0, 0)
> +		call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>  
>  unhandled_count = 0
>  
> @@ -397,6 +400,7 @@ def trace_end():
>  	print datetime.datetime.today(), "Adding indexes"
>  	if perf_db_export_calls:
>  		do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
> +		do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
>  
>  	if (unhandled_count):
>  		print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
> @@ -452,4 +456,4 @@ def call_path_table(*x):
>  	bind_exec(call_path_query, 4, x)
>  
>  def call_return_table(*x):
> -	bind_exec(call_query, 11, x)
> +	bind_exec(call_query, 12, x)
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 4/8] perf scripts python: export-to-postgresql.py: Export calls parent_id
       [not found] ` <20190228130031.23064-5-adrian.hunter@intel.com>
@ 2019-03-01 17:54   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:54 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:27PM +0200, Adrian Hunter escreveu:
> Export to the 'calls' table the newly created 'parent_id' and create an
> index for it.

Thanks, applied.

- Arnaldo
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  tools/perf/scripts/python/export-to-postgresql.py | 14 +++++++++-----
>  1 file changed, 9 insertions(+), 5 deletions(-)
> 
> diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
> index 6358522a69f6..390a351d15ea 100644
> --- a/tools/perf/scripts/python/export-to-postgresql.py
> +++ b/tools/perf/scripts/python/export-to-postgresql.py
> @@ -394,7 +394,8 @@ if perf_db_export_calls:
>  		'call_id	bigint,'
>  		'return_id	bigint,'
>  		'parent_call_path_id	bigint,'
> -		'flags		integer)')
> +		'flags		integer,'
> +		'parent_id	bigint)')
>  
>  do_query(query, 'CREATE VIEW machines_view AS '
>  	'SELECT '
> @@ -479,7 +480,8 @@ if perf_db_export_calls:
>  			'call_id,'
>  			'return_id,'
>  			'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE CAST ( flags AS VARCHAR(6) ) END AS flags,'
> -			'parent_call_path_id'
> +			'parent_call_path_id,'
> +			'calls.parent_id'
>  		' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
>  
>  do_query(query, 'CREATE VIEW samples_view AS '
> @@ -575,6 +577,7 @@ def trace_begin():
>  	sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>  	if perf_db_export_calls or perf_db_export_callchains:
>  		call_path_table(0, 0, 0, 0)
> +		call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>  
>  unhandled_count = 0
>  
> @@ -657,6 +660,7 @@ def trace_end():
>  					'ADD CONSTRAINT returnfk    FOREIGN KEY (return_id)    REFERENCES samples    (id),'
>  					'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
>  		do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
> +		do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
>  
>  	if (unhandled_count):
>  		print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
> @@ -728,7 +732,7 @@ def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
>  	value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip)
>  	call_path_file.write(value)
>  
> -def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x):
> -	fmt = "!hiqiqiqiqiqiqiqiqiqiqii"
> -	value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags)
> +def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, parent_id, *x):
> +	fmt = "!hiqiqiqiqiqiqiqiqiqiqiiiq"
> +	value = struct.pack(fmt, 12, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags, 8, parent_id)
>  	call_file.write(value)
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 3/8] perf scripts python: export-to-postgresql.py: Fix invalid input syntax for integer error
       [not found] ` <20190228130031.23064-4-adrian.hunter@intel.com>
@ 2019-03-01 17:54   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:54 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:26PM +0200, Adrian Hunter escreveu:
> Fix SQL query error "invalid input syntax for integer":
> 
>   Traceback (most recent call last):
>     File "tools/perf/scripts/python/export-to-postgresql.py", line 465, in <module>
>       do_query(query, 'CREATE VIEW calls_view AS '
>     File "tools/perf/scripts/python/export-to-postgresql.py", line 274, in do_query
>       raise Exception("Query failed: " + q.lastError().text())
>   Exception: Query failed: ERROR:  invalid input syntax for integer: ""
>   LINE 1: ...ch_count,call_id,return_id,CASE WHEN flags=0 THEN '' WHEN fl...
>                                                                ^
>   (22P02) QPSQL: Unable to create query
>   Error running python script tools/perf/scripts/python/export-to-postgresql.py

Thanks, applied.

- Arnaldo
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> Fixes: f08046cb3082 ("perf thread-stack: Represent jmps to the start of a different symbol")
> ---
>  tools/perf/scripts/python/export-to-postgresql.py | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
> index 30130213da7e..6358522a69f6 100644
> --- a/tools/perf/scripts/python/export-to-postgresql.py
> +++ b/tools/perf/scripts/python/export-to-postgresql.py
> @@ -478,7 +478,7 @@ if perf_db_export_calls:
>  			'branch_count,'
>  			'call_id,'
>  			'return_id,'
> -			'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE flags END AS flags,'
> +			'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE CAST ( flags AS VARCHAR(6) ) END AS flags,'
>  			'parent_call_path_id'
>  		' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
>  
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 5/8] perf scripts python: exported-sql-viewer.py: Factor out TreeWindowBase
       [not found] ` <20190228130031.23064-6-adrian.hunter@intel.com>
@ 2019-03-01 17:55   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:55 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:28PM +0200, Adrian Hunter escreveu:
> Factor out a base class TreeWindowBase from CallGraphWindow, so that
> TreeWindowBase can be reused.

Thanks, applied.

- Arnaldo
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  .../scripts/python/exported-sql-viewer.py     | 50 ++++++++++++-------
>  1 file changed, 31 insertions(+), 19 deletions(-)
> 
> diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
> index 09ce73b07d35..df854f0a69f0 100755
> --- a/tools/perf/scripts/python/exported-sql-viewer.py
> +++ b/tools/perf/scripts/python/exported-sql-viewer.py
> @@ -693,28 +693,16 @@ class VBox():
>  	def Widget(self):
>  		return self.vbox
>  
> -# Context-sensitive call graph window
> -
> -class CallGraphWindow(QMdiSubWindow):
> -
> -	def __init__(self, glb, parent=None):
> -		super(CallGraphWindow, self).__init__(parent)
> +# Tree window base
>  
> -		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
> +class TreeWindowBase(QMdiSubWindow):
>  
> -		self.view = QTreeView()
> -		self.view.setModel(self.model)
> -
> -		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
> -			self.view.setColumnWidth(c, w)
> -
> -		self.find_bar = FindBar(self, self)
> -
> -		self.vbox = VBox(self.view, self.find_bar.Widget())
> -
> -		self.setWidget(self.vbox.Widget())
> +	def __init__(self, parent=None):
> +		super(TreeWindowBase, self).__init__(parent)
>  
> -		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
> +		self.model = None
> +		self.view = None
> +		self.find_bar = None
>  
>  	def DisplayFound(self, ids):
>  		if not len(ids):
> @@ -747,6 +735,30 @@ class CallGraphWindow(QMdiSubWindow):
>  		if not found:
>  			self.find_bar.NotFound()
>  
> +
> +# Context-sensitive call graph window
> +
> +class CallGraphWindow(TreeWindowBase):
> +
> +	def __init__(self, glb, parent=None):
> +		super(CallGraphWindow, self).__init__(parent)
> +
> +		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
> +
> +		self.view = QTreeView()
> +		self.view.setModel(self.model)
> +
> +		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
> +			self.view.setColumnWidth(c, w)
> +
> +		self.find_bar = FindBar(self, self)
> +
> +		self.vbox = VBox(self.view, self.find_bar.Widget())
> +
> +		self.setWidget(self.vbox.Widget())
> +
> +		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
> +
>  # Child data item  finder
>  
>  class ChildDataItemFinder():
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 6/8] perf scripts python: exported-sql-viewer.py: Improve TreeModel abstraction
       [not found] ` <20190228130031.23064-7-adrian.hunter@intel.com>
@ 2019-03-01 17:55   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:55 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:29PM +0200, Adrian Hunter escreveu:
> Instead of passing the tree root, get it from a method that can be
> implemented in any derived class.

Thanks, applied.

- Arnaldo
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  .../perf/scripts/python/exported-sql-viewer.py  | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
> 
> diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
> index df854f0a69f0..b2a22525549d 100755
> --- a/tools/perf/scripts/python/exported-sql-viewer.py
> +++ b/tools/perf/scripts/python/exported-sql-viewer.py
> @@ -167,9 +167,10 @@ class Thread(QThread):
>  
>  class TreeModel(QAbstractItemModel):
>  
> -	def __init__(self, root, parent=None):
> +	def __init__(self, glb, parent=None):
>  		super(TreeModel, self).__init__(parent)
> -		self.root = root
> +		self.glb = glb
> +		self.root = self.GetRoot()
>  		self.last_row_read = 0
>  
>  	def Item(self, parent):
> @@ -562,8 +563,10 @@ class CallGraphRootItem(CallGraphLevelItemBase):
>  class CallGraphModel(TreeModel):
>  
>  	def __init__(self, glb, parent=None):
> -		super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent)
> -		self.glb = glb
> +		super(CallGraphModel, self).__init__(glb, parent)
> +
> +	def GetRoot(self):
> +		return CallGraphRootItem(self.glb)
>  
>  	def columnCount(self, parent=None):
>  		return 7
> @@ -1339,8 +1342,7 @@ class BranchModel(TreeModel):
>  	progress = Signal(object)
>  
>  	def __init__(self, glb, event_id, where_clause, parent=None):
> -		super(BranchModel, self).__init__(BranchRootItem(), parent)
> -		self.glb = glb
> +		super(BranchModel, self).__init__(glb, parent)
>  		self.event_id = event_id
>  		self.more = True
>  		self.populated = 0
> @@ -1364,6 +1366,9 @@ class BranchModel(TreeModel):
>  		self.fetcher.done.connect(self.Update)
>  		self.fetcher.Fetch(glb_chunk_sz)
>  
> +	def GetRoot(self):
> +		return BranchRootItem()
> +
>  	def columnCount(self, parent=None):
>  		return 8
>  
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 7/8] perf scripts python: exported-sql-viewer.py: Factor out CallGraphModelBase
       [not found] ` <20190228130031.23064-8-adrian.hunter@intel.com>
@ 2019-03-01 17:56   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 17:56 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:30PM +0200, Adrian Hunter escreveu:
> Factor out a base class CallGraphModelBase from CallGraphModel, so that
> CallGraphModelBase can be reused.

Thanks, applied.

- Arnaldo
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  .../scripts/python/exported-sql-viewer.py     | 100 ++++++++++--------
>  1 file changed, 55 insertions(+), 45 deletions(-)
> 
> diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
> index b2a22525549d..c4a2134d85f5 100755
> --- a/tools/perf/scripts/python/exported-sql-viewer.py
> +++ b/tools/perf/scripts/python/exported-sql-viewer.py
> @@ -558,26 +558,12 @@ class CallGraphRootItem(CallGraphLevelItemBase):
>  			self.child_items.append(child_item)
>  			self.child_count += 1
>  
> -# Context-sensitive call graph data model
> +# Context-sensitive call graph data model base
>  
> -class CallGraphModel(TreeModel):
> +class CallGraphModelBase(TreeModel):
>  
>  	def __init__(self, glb, parent=None):
> -		super(CallGraphModel, self).__init__(glb, parent)
> -
> -	def GetRoot(self):
> -		return CallGraphRootItem(self.glb)
> -
> -	def columnCount(self, parent=None):
> -		return 7
> -
> -	def columnHeader(self, column):
> -		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
> -		return headers[column]
> -
> -	def columnAlignment(self, column):
> -		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
> -		return alignment[column]
> +		super(CallGraphModelBase, self).__init__(glb, parent)
>  
>  	def FindSelect(self, value, pattern, query):
>  		if pattern:
> @@ -597,34 +583,7 @@ class CallGraphModel(TreeModel):
>  				match = " GLOB '" + str(value) + "'"
>  		else:
>  			match = " = '" + str(value) + "'"
> -		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
> -						" FROM calls"
> -						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
> -						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
> -						" WHERE symbols.name" + match +
> -						" GROUP BY comm_id, thread_id, call_path_id"
> -						" ORDER BY comm_id, thread_id, call_path_id")
> -
> -	def FindPath(self, query):
> -		# Turn the query result into a list of ids that the tree view can walk
> -		# to open the tree at the right place.
> -		ids = []
> -		parent_id = query.value(0)
> -		while parent_id:
> -			ids.insert(0, parent_id)
> -			q2 = QSqlQuery(self.glb.db)
> -			QueryExec(q2, "SELECT parent_id"
> -					" FROM call_paths"
> -					" WHERE id = " + str(parent_id))
> -			if not q2.next():
> -				break
> -			parent_id = q2.value(0)
> -		# The call path root is not used
> -		if ids[0] == 1:
> -			del ids[0]
> -		ids.insert(0, query.value(2))
> -		ids.insert(0, query.value(1))
> -		return ids
> +		self.DoFindSelect(query, match)
>  
>  	def Found(self, query, found):
>  		if found:
> @@ -678,6 +637,57 @@ class CallGraphModel(TreeModel):
>  	def FindDone(self, thread, callback, ids):
>  		callback(ids)
>  
> +# Context-sensitive call graph data model
> +
> +class CallGraphModel(CallGraphModelBase):
> +
> +	def __init__(self, glb, parent=None):
> +		super(CallGraphModel, self).__init__(glb, parent)
> +
> +	def GetRoot(self):
> +		return CallGraphRootItem(self.glb)
> +
> +	def columnCount(self, parent=None):
> +		return 7
> +
> +	def columnHeader(self, column):
> +		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
> +		return headers[column]
> +
> +	def columnAlignment(self, column):
> +		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
> +		return alignment[column]
> +
> +	def DoFindSelect(self, query, match):
> +		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
> +						" FROM calls"
> +						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
> +						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
> +						" WHERE symbols.name" + match +
> +						" GROUP BY comm_id, thread_id, call_path_id"
> +						" ORDER BY comm_id, thread_id, call_path_id")
> +
> +	def FindPath(self, query):
> +		# Turn the query result into a list of ids that the tree view can walk
> +		# to open the tree at the right place.
> +		ids = []
> +		parent_id = query.value(0)
> +		while parent_id:
> +			ids.insert(0, parent_id)
> +			q2 = QSqlQuery(self.glb.db)
> +			QueryExec(q2, "SELECT parent_id"
> +					" FROM call_paths"
> +					" WHERE id = " + str(parent_id))
> +			if not q2.next():
> +				break
> +			parent_id = q2.value(0)
> +		# The call path root is not used
> +		if ids[0] == 1:
> +			del ids[0]
> +		ids.insert(0, query.value(2))
> +		ids.insert(0, query.value(1))
> +		return ids
> +
>  # Vertical widget layout
>  
>  class VBox():
> -- 
> 2.17.1

-- 

- Arnaldo

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

* Re: [PATCH 8/8] perf scripts python: exported-sql-viewer.py: Add call tree
       [not found] ` <20190228130031.23064-9-adrian.hunter@intel.com>
@ 2019-03-01 18:20   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2019-03-01 18:20 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Jiri Olsa, Linux Kernel Mailing List

Em Thu, Feb 28, 2019 at 03:00:31PM +0200, Adrian Hunter escreveu:
> Add a new report to display a call tree. The Call Tree report is very
> similar to the Context-Sensitive Call Graph, but the data is not
> aggregated. Also the 'Count' column, which would be always 1, is replaced
> by the 'Call Time'.

Thanks, applied and added committer testing notes:


    Committer testing:
    
      $ cat simple-retpoline.c
      /*
    
        https://lkml.kernel.org/r/20190109091835.5570-6-adrian.hunter@intel.com
    
      $ gcc -ggdb3 -Wall -Wextra -O2 -o simple-retpoline simple-retpoline.c
      $ objdump -d simple-retpoline
      */
    
      __attribute__((noinline)) int bar(void)
      {
              return -1;
      }
    
      int foo(void)
      {
              return bar() + 1;
      }
    
      __attribute__((indirect_branch("thunk"))) int main()
      {
              int (*volatile fn)(void) = foo;
    
              fn();
              return fn();
      }
      $
      $ perf record -o simple-retpoline.perf.data -e intel_pt/cyc/u ./simple-retpoline
      $ perf script -i simple-retpoline.perf.data --itrace=be -s ~acme/libexec/perf-core/scripts/python/export-to-sqlite.py simple-retpoline.db branches calls
      $ python ~acme/libexec/perf-core/scripts/python/exported-sql-viewer.py simple-retpoline.db
    
    And in the GUI select:
    
        "Reports"
          "Call Tree"
    
        Call Path                 | Object          | Call Time (ns) | Time (ns) | Time (%) | Branch Count | Brach Count (%) |
        > simple-retpolin
          > PID:TID
            > _start                ld-2.28.so       2193855505777      156267      100.0       10602          100.0
                unknown             unknown          2193855506010        2276        1.5           1            0.0
              > _dl_start           ld-2.28.so       2193855508286      137047       87.7       10088           95.2
              > _dl_init            ld-2.28.so       2193855645444        9142        5.9         326            3.1
              > _start              simple-retpoline 2193855654587        7457        4.8         182            1.7
                > __libc_start_main <SNIP>
                  <SNIP>
                  > main            simple-retpoline 2193855657493          32        0.5          12            6.7
                    > foo           simple-retpoline 2193855657493          14       43.8           5           41.7
                  <SNIP>
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  .../scripts/python/exported-sql-viewer.py     | 195 +++++++++++++++++-
>  1 file changed, 186 insertions(+), 9 deletions(-)
> 
> diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
> index c4a2134d85f5..afec9479ca7f 100755
> --- a/tools/perf/scripts/python/exported-sql-viewer.py
> +++ b/tools/perf/scripts/python/exported-sql-viewer.py
> @@ -688,6 +688,150 @@ class CallGraphModel(CallGraphModelBase):
>  		ids.insert(0, query.value(1))
>  		return ids
>  
> +# Call tree data model level 2+ item base
> +
> +class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
> +
> +	def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item):
> +		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
> +		self.comm_id = comm_id
> +		self.thread_id = thread_id
> +		self.calls_id = calls_id
> +		self.branch_count = branch_count
> +		self.time = time
> +
> +	def Select(self):
> +		self.query_done = True;
> +		if self.calls_id == 0:
> +			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
> +		else:
> +			comm_thread = ""
> +		query = QSqlQuery(self.glb.db)
> +		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count"
> +					" FROM calls"
> +					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
> +					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
> +					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
> +					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
> +					" ORDER BY call_time, calls.id")
> +		while query.next():
> +			child_item = CallTreeLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
> +			self.child_items.append(child_item)
> +			self.child_count += 1
> +
> +# Call tree data model level three item
> +
> +class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
> +
> +	def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item):
> +		super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item)
> +		dso = dsoname(dso)
> +		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
> +		self.dbid = calls_id
> +
> +# Call tree data model level two item
> +
> +class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
> +
> +	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
> +		super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item)
> +		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
> +		self.dbid = thread_id
> +
> +	def Select(self):
> +		super(CallTreeLevelTwoItem, self).Select()
> +		for child_item in self.child_items:
> +			self.time += child_item.time
> +			self.branch_count += child_item.branch_count
> +		for child_item in self.child_items:
> +			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
> +			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
> +
> +# Call tree data model level one item
> +
> +class CallTreeLevelOneItem(CallGraphLevelItemBase):
> +
> +	def __init__(self, glb, row, comm_id, comm, parent_item):
> +		super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item)
> +		self.data = [comm, "", "", "", "", "", ""]
> +		self.dbid = comm_id
> +
> +	def Select(self):
> +		self.query_done = True;
> +		query = QSqlQuery(self.glb.db)
> +		QueryExec(query, "SELECT thread_id, pid, tid"
> +					" FROM comm_threads"
> +					" INNER JOIN threads ON thread_id = threads.id"
> +					" WHERE comm_id = " + str(self.dbid))
> +		while query.next():
> +			child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
> +			self.child_items.append(child_item)
> +			self.child_count += 1
> +
> +# Call tree data model root item
> +
> +class CallTreeRootItem(CallGraphLevelItemBase):
> +
> +	def __init__(self, glb):
> +		super(CallTreeRootItem, self).__init__(glb, 0, None)
> +		self.dbid = 0
> +		self.query_done = True;
> +		query = QSqlQuery(glb.db)
> +		QueryExec(query, "SELECT id, comm FROM comms")
> +		while query.next():
> +			if not query.value(0):
> +				continue
> +			child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
> +			self.child_items.append(child_item)
> +			self.child_count += 1
> +
> +# Call Tree data model
> +
> +class CallTreeModel(CallGraphModelBase):
> +
> +	def __init__(self, glb, parent=None):
> +		super(CallTreeModel, self).__init__(glb, parent)
> +
> +	def GetRoot(self):
> +		return CallTreeRootItem(self.glb)
> +
> +	def columnCount(self, parent=None):
> +		return 7
> +
> +	def columnHeader(self, column):
> +		headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
> +		return headers[column]
> +
> +	def columnAlignment(self, column):
> +		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
> +		return alignment[column]
> +
> +	def DoFindSelect(self, query, match):
> +		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
> +						" FROM calls"
> +						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
> +						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
> +						" WHERE symbols.name" + match +
> +						" ORDER BY comm_id, thread_id, call_time, calls.id")
> +
> +	def FindPath(self, query):
> +		# Turn the query result into a list of ids that the tree view can walk
> +		# to open the tree at the right place.
> +		ids = []
> +		parent_id = query.value(0)
> +		while parent_id:
> +			ids.insert(0, parent_id)
> +			q2 = QSqlQuery(self.glb.db)
> +			QueryExec(q2, "SELECT parent_id"
> +					" FROM calls"
> +					" WHERE id = " + str(parent_id))
> +			if not q2.next():
> +				break
> +			parent_id = q2.value(0)
> +		ids.insert(0, query.value(2))
> +		ids.insert(0, query.value(1))
> +		return ids
> +
>  # Vertical widget layout
>  
>  class VBox():
> @@ -772,6 +916,29 @@ class CallGraphWindow(TreeWindowBase):
>  
>  		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
>  
> +# Call tree window
> +
> +class CallTreeWindow(TreeWindowBase):
> +
> +	def __init__(self, glb, parent=None):
> +		super(CallTreeWindow, self).__init__(parent)
> +
> +		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
> +
> +		self.view = QTreeView()
> +		self.view.setModel(self.model)
> +
> +		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
> +			self.view.setColumnWidth(c, w)
> +
> +		self.find_bar = FindBar(self, self)
> +
> +		self.vbox = VBox(self.view, self.find_bar.Widget())
> +
> +		self.setWidget(self.vbox.Widget())
> +
> +		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
> +
>  # Child data item  finder
>  
>  class ChildDataItemFinder():
> @@ -1890,10 +2057,10 @@ def GetEventList(db):
>  
>  # Is a table selectable
>  
> -def IsSelectable(db, table):
> +def IsSelectable(db, table, sql = ""):
>  	query = QSqlQuery(db)
>  	try:
> -		QueryExec(query, "SELECT * FROM " + table + " LIMIT 1")
> +		QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1")
>  	except:
>  		return False
>  	return True
> @@ -2302,9 +2469,10 @@ p.c2 {
>  </style>
>  <p class=c1><a href=#reports>1. Reports</a></p>
>  <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
> -<p class=c2><a href=#allbranches>1.2 All branches</a></p>
> -<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
> -<p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p>
> +<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
> +<p class=c2><a href=#allbranches>1.3 All branches</a></p>
> +<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
> +<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
>  <p class=c1><a href=#tables>2. Tables</a></p>
>  <h1 id=reports>1. Reports</h1>
>  <h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
> @@ -2340,7 +2508,10 @@ v- ls
>  <h3>Find</h3>
>  Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
>  The pattern matching symbols are ? for any character and * for zero or more characters.
> -<h2 id=allbranches>1.2 All branches</h2>
> +<h2 id=calltree>1.2 Call Tree</h2>
> +The Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
> +Also the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
> +<h2 id=allbranches>1.3 All branches</h2>
>  The All branches report displays all branches in chronological order.
>  Not all data is fetched immediately. More records can be fetched using the Fetch bar provided.
>  <h3>Disassembly</h3>
> @@ -2366,10 +2537,10 @@ sudo ldconfig
>  Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
>  Refer to Python documentation for the regular expression syntax.
>  All columns are searched, but only currently fetched rows are searched.
> -<h2 id=selectedbranches>1.3 Selected branches</h2>
> +<h2 id=selectedbranches>1.4 Selected branches</h2>
>  This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
>  by various selection criteria. A dialog box displays available criteria which are AND'ed together.
> -<h3>1.3.1 Time ranges</h3>
> +<h3>1.4.1 Time ranges</h3>
>  The time ranges hint text shows the total time range. Relative time ranges can also be entered in
>  ms, us or ns. Also, negative values are relative to the end of trace.  Examples:
>  <pre>
> @@ -2380,7 +2551,7 @@ ms, us or ns. Also, negative values are relative to the end of trace.  Examples:
>  	-10ms-			The last 10ms
>  </pre>
>  N.B. Due to the granularity of timestamps, there could be no branches in any given time range.
> -<h2 id=topcallsbyelapsedtime>1.4 Top calls by elapsed time</h2>
> +<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
>  The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
>  The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
>  If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
> @@ -2516,6 +2687,9 @@ class MainWindow(QMainWindow):
>  		if IsSelectable(glb.db, "calls"):
>  			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
>  
> +		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
> +			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
> +
>  		self.EventMenu(GetEventList(glb.db), reports_menu)
>  
>  		if IsSelectable(glb.db, "calls"):
> @@ -2576,6 +2750,9 @@ class MainWindow(QMainWindow):
>  	def NewCallGraph(self):
>  		CallGraphWindow(self.glb, self)
>  
> +	def NewCallTree(self):
> +		CallTreeWindow(self.glb, self)
> +
>  	def NewTopCalls(self):
>  		dialog = TopCallsDialog(self.glb, self)
>  		ret = dialog.exec_()
> -- 
> 2.17.1

-- 

- Arnaldo

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

end of thread, other threads:[~2019-03-01 18:20 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20190228130031.23064-1-adrian.hunter@intel.com>
     [not found] ` <20190228130031.23064-2-adrian.hunter@intel.com>
2019-03-01 17:52   ` [PATCH 1/8] perf db-export: Add calls parent_id Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-3-adrian.hunter@intel.com>
2019-03-01 17:52   ` [PATCH 2/8] perf scripts python: export-to-sqlite.py: Export " Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-5-adrian.hunter@intel.com>
2019-03-01 17:54   ` [PATCH 4/8] perf scripts python: export-to-postgresql.py: " Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-4-adrian.hunter@intel.com>
2019-03-01 17:54   ` [PATCH 3/8] perf scripts python: export-to-postgresql.py: Fix invalid input syntax for integer error Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-6-adrian.hunter@intel.com>
2019-03-01 17:55   ` [PATCH 5/8] perf scripts python: exported-sql-viewer.py: Factor out TreeWindowBase Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-7-adrian.hunter@intel.com>
2019-03-01 17:55   ` [PATCH 6/8] perf scripts python: exported-sql-viewer.py: Improve TreeModel abstraction Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-8-adrian.hunter@intel.com>
2019-03-01 17:56   ` [PATCH 7/8] perf scripts python: exported-sql-viewer.py: Factor out CallGraphModelBase Arnaldo Carvalho de Melo
     [not found] ` <20190228130031.23064-9-adrian.hunter@intel.com>
2019-03-01 18:20   ` [PATCH 8/8] perf scripts python: exported-sql-viewer.py: Add call tree Arnaldo Carvalho de Melo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).