perf top: Show warning in display thread

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 389da42..751282f 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -149,18 +149,22 @@ static void __zero_source_counters(struct hist_entry *he)
 	symbol__annotate_zero_histograms(sym);
 }
 
-static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
+static void perf_top__init_warning(struct perf_top *top)
 {
-	char *warn_msg;
+	pthread_mutex_init(&top->warning.lock, NULL);
+	top->warning.code = PERF_TOP_WARN__NONE;
+}
 
-	warn_msg = perf_top__warn_map_erange(map, sym, ip);
-	ui__warning("%s", warn_msg ?: "Out of bounds address found\n");
-	free(warn_msg);
-
-	if (use_browser <= 0)
-		sleep(5);
-
-	map->erange_warned = true;
+static void perf_top__set_warning(struct perf_top *top,
+				  enum perf_top_warning code,
+				  struct map *map, struct symbol *sym, u64 ip)
+{
+	pthread_mutex_lock(&top->warning.lock);
+	top->warning.code = code;
+	top->warning.map  = map;
+	top->warning.sym  = sym;
+	top->warning.ip   = ip;
+	pthread_mutex_unlock(&top->warning.lock);
 }
 
 static void perf_top__record_precise_ip(struct perf_top *top,
@@ -193,9 +197,11 @@ static void perf_top__record_precise_ip(struct perf_top *top,
 		 */
 		pthread_mutex_unlock(&he->hists->lock);
 
-		if (err == -ERANGE && !he->ms.map->erange_warned)
-			ui__warn_map_erange(he->ms.map, sym, ip);
-		else if (err == -ENOMEM) {
+		if (err == -ERANGE && !he->ms.map->erange_warned) {
+			perf_top__set_warning(top, PERF_TOP_WARN__MAP_ERANGE,
+					      he->ms.map, sym, ip);
+			he->ms.map->erange_warned = true;
+		} else if (err == -ENOMEM) {
 			pr_err("Not enough memory for annotating '%s' symbol!\n",
 			       sym->name);
 		}
@@ -714,14 +720,8 @@ static void perf_event__process_sample(struct perf_tool *tool,
 	    symbol_conf.kptr_restrict &&
 	    al.cpumode == PERF_RECORD_MISC_KERNEL) {
 		if (!perf_evlist__exclude_kernel(top->session->evlist)) {
-			char *warn_msg;
-
-			warn_msg = perf_top__warn_kptr_restrict(al.map);
-			ui__warning("%s", warn_msg ?: "Kernel maps are restricted.\n");
-			free(warn_msg);
-
-			if (use_browser <= 0)
-				sleep(5);
+			perf_top__set_warning(top, PERF_TOP_WARN__KPTR_RESTRICT,
+					      al.map, NULL, 0);
 		}
 		machine->kptr_restrict_warned = true;
 	}
@@ -740,14 +740,8 @@ static void perf_event__process_sample(struct perf_tool *tool,
 		 */
 		if (!machine->kptr_restrict_warned && !top->vmlinux_warned &&
 		    __map__is_kernel(al.map) && map__has_symbols(al.map)) {
-			char *warn_msg;
-
-			warn_msg = perf_top__warn_vmlinux(al.map);
-			ui__warning("%s", warn_msg ?: "Kernel symbols will not be resolved.\n");
-			free(warn_msg);
-
-			if (use_browser <= 0)
-				sleep(5);
+			perf_top__set_warning(top, PERF_TOP_WARN__VMLINUX,
+					      al.map, NULL, 0);
 			top->vmlinux_warned = true;
 		}
 	}
@@ -875,11 +869,8 @@ static void perf_top__mmap_read(struct perf_top *top)
 	end = rdclock();
 
 	if ((end - start) > (unsigned long long)top->delay_secs * NSEC_PER_SEC) {
-		char *warn_msg;
-
-		warn_msg = perf_top__warn_mmap_read();
-		ui__warning("%s", warn_msg ?: "Too slow to read ring buffer.\n");
-		free(warn_msg);
+		perf_top__set_warning(top, PERF_TOP_WARN__MMAP_READ,
+				      NULL, NULL, 0);
 	}
 }
 
@@ -1493,6 +1484,8 @@ int cmd_top(int argc, const char **argv)
 		signal(SIGWINCH, winch_sig);
 	}
 
+	perf_top__init_warning(&top);
+
 	status = __cmd_top(&top);
 
 out_delete_evlist:
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index a96f62c..e20ed33 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -606,6 +606,73 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
 		"Or reduce the sampling frequency.");
 }
 
+static void ui_browser__perf_top_warn(struct ui_browser *browser,
+				      struct perf_top *top)
+{
+	enum perf_top_warning code;
+	struct map *map;
+	struct symbol *sym;
+	u64 ip;
+	const char *msg;
+	bool alloc = true;
+
+	pthread_mutex_lock(&top->warning.lock);
+	code = top->warning.code;
+	map  = top->warning.map;
+	sym  = top->warning.sym;
+	ip   = top->warning.ip;
+
+	top->warning.code = PERF_TOP_WARN__NONE;
+	pthread_mutex_unlock(&top->warning.lock);
+
+	if (code == PERF_TOP_WARN__NONE)
+		return;
+
+	switch (code) {
+	case PERF_TOP_WARN__MAP_ERANGE:
+		msg = perf_top__warn_map_erange(map, sym, ip);
+		if (msg == NULL) {
+			msg = "Out of bounds address found.\n";
+			alloc = false;
+		}
+		break;
+
+	case PERF_TOP_WARN__KPTR_RESTRICT:
+		msg = perf_top__warn_kptr_restrict(map);
+		if (msg == NULL) {
+			msg = "Kernel maps are restricted.\n";
+			alloc = false;
+		}
+		break;
+
+	case PERF_TOP_WARN__VMLINUX:
+		msg = perf_top__warn_vmlinux(map);
+		if (msg == NULL) {
+			msg = "Kernel symbols will not be resolved.\n";
+			alloc = false;
+		}
+		break;
+
+	case PERF_TOP_WARN__MMAP_READ:
+		msg = perf_top__warn_mmap_read();
+		if (msg == NULL) {
+			msg = "Too slow to read ring buffer.\n";
+			alloc = false;
+		}
+		break;
+
+	case PERF_TOP_WARN__NONE:
+	case PERF_TOP_WARN__MAX:
+	default:
+		return;
+	}
+
+	ui_browser__warning(browser, 5, "%s", msg);
+
+	if (alloc)
+		free((void *)msg);
+}
+
 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
 {
 	return browser->title ? browser->title(browser, bf, size) : 0;
@@ -642,6 +709,11 @@ int hist_browser__run(struct hist_browser *browser, const char *help,
 			nr_entries = hist_browser__nr_entries(browser);
 			ui_browser__update_nr_entries(&browser->b, nr_entries);
 
+			hist_browser__title(browser, title, sizeof(title));
+			ui_browser__show_title(&browser->b, title);
+
+			ui_browser__perf_top_warn(&browser->b, hbt->arg);
+
 			if (warn_lost_event &&
 			    (browser->hists->stats.nr_lost_warned !=
 			    browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
@@ -650,8 +722,6 @@ int hist_browser__run(struct hist_browser *browser, const char *help,
 				ui_browser__warn_lost_events(&browser->b);
 			}
 
-			hist_browser__title(browser, title, sizeof(title));
-			ui_browser__show_title(&browser->b, title);
 			continue;
 		}
 		case 'D': { /* Debug */
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index 5a5a98a..0dd80ae 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -13,6 +13,16 @@ struct perf_evlist;
 struct perf_evsel;
 struct perf_session;
 
+enum perf_top_warning {
+	PERF_TOP_WARN__NONE		= 0,
+	PERF_TOP_WARN__MAP_ERANGE,
+	PERF_TOP_WARN__KPTR_RESTRICT,
+	PERF_TOP_WARN__VMLINUX,
+	PERF_TOP_WARN__MMAP_READ,
+
+	PERF_TOP_WARN__MAX,
+};
+
 struct perf_top {
 	struct perf_tool   tool;
 	struct perf_evlist *evlist;
@@ -40,6 +50,14 @@ struct perf_top {
 	const char	   *sym_filter;
 	float		   min_percent;
 	unsigned int	   nr_threads_synthesize;
+
+	struct {
+		pthread_mutex_t		lock;
+		enum perf_top_warning	code;
+		struct map		*map;
+		struct symbol		*sym;
+		u64			ip;
+	} warning;
 };
 
 #define CONSOLE_CLEAR ""