From 338aa477321c0cd0cfe159aead7b6d616a81335b Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 5 Jan 2012 10:55:15 +0100 Subject: [observer] Started with system monitor --- lib/observer/src/Makefile | 1 + lib/observer/src/observer_perf_wx.erl | 370 ++++++++++++++++++++++++++++++++++ lib/observer/src/observer_wx.erl | 11 +- 3 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 lib/observer/src/observer_perf_wx.erl diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index ca26afc11d..7eb2144dee 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -45,6 +45,7 @@ MODULES= \ observer_app_wx \ observer_lib \ observer_wx \ + observer_perf_wx \ observer_pro_wx \ observer_procinfo \ observer_sys_wx \ diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl new file mode 100644 index 0000000000..04e09f18d8 --- /dev/null +++ b/lib/observer/src/observer_perf_wx.erl @@ -0,0 +1,370 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +-module(observer_perf_wx). + +-export([start_link/2]). + +%% wx_object callbacks +-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, + handle_event/2, handle_sync_event/3, handle_cast/2]). + +-behaviour(wx_object). +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +-compile(export_all). + +-record(state, + { + parent, + windows, + data = {0, queue:new()}, + panel, + paint, + appmon + }). + +-record(paint, {font, pen, pens}). + +-define(RQ_W, 1). +-define(MEM_W, 2). +-define(IO_W, 3). + +start_link(Notebook, Parent) -> + wx_object:start_link(?MODULE, [Notebook, Parent], []). + +init([Notebook, Parent]) -> + try + Panel = wxPanel:new(Notebook), + %% wxWindow:setBackgroundColour(Panel, {222,222,222}), + Main = wxBoxSizer:new(?wxVERTICAL), + + CPU = wxPanel:new(Panel, [{winid, ?RQ_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), + wxWindow:setBackgroundColour(CPU, ?wxWHITE), + wxSizer:add(Main, CPU, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}, {border, 5}]), + MemIO = wxBoxSizer:new(?wxHORIZONTAL), + MEM = wxPanel:new(Panel, [{winid, ?MEM_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), + wxWindow:setBackgroundColour(MEM, ?wxWHITE), + IO = wxPanel:new(Panel, [{winid, ?IO_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), + wxWindow:setBackgroundColour(IO, ?wxWHITE), + wxSizer:add(MemIO, MEM, [{flag, ?wxEXPAND bor ?wxLEFT}, + {proportion, 1}, {border, 5}]), + wxSizer:add(MemIO, IO, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT}, + {proportion, 1}, {border, 5}]), + wxSizer:add(Main, MemIO, [{flag, ?wxEXPAND bor ?wxDOWN}, + {proportion, 1}, {border, 5}]), + wxWindow:setSizer(Panel, Main), + + wxPanel:connect(CPU, paint, [callback]), + wxPanel:connect(IO, paint, [callback]), + wxPanel:connect(MEM, paint, [callback]), + % wxPanel:connect(DrawingArea, size, [{skip, true}]), + + DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + Cols = [{220, 50, 50}, {220, 50, 220}, {50, 50, 220}, + {50, 220, 220}, {50, 220, 50}, {220, 220, 50}], + Pens = [wxPen:new(Col) || Col <- Cols], + %% GC = wxGraphicsContext:create(DrawingArea), + %% _Font = wxGraphicsContext:createFont(GC, DefFont), + {Panel, #state{parent=Parent, + panel =Panel, + windows = {CPU, MEM, IO}, + paint=#paint{font= DefFont, + pen = ?wxBLACK_PEN, + pens = list_to_tuple(Pens) + } + }} + catch _:Err -> + io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), + {error, Err} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% handle_event(#wx{id=Id, event=_Sz=#wxSize{size=Size}}, +%% State=#state{}) -> +%% %% Id =:= ?DRAWAREA andalso setup_scrollbar(Size,AppWin,App), +%% {noreply, State}; + +handle_event(#wx{event=#wxCommand{type=command_menu_selected}}, + State = #state{}) -> + {noreply, State}; + +handle_event(Event, _State) -> + error({unhandled_event, Event}). + +%%%%%%%%%% +handle_sync_event(#wx{id=Id, event = #wxPaint{}},_, + #state{paint=Paint, windows=Windows, data=Data}) -> + %% PaintDC must be created in a callback to work on windows. + Panel = element(Id, Windows), + DC = wxPaintDC:new(Panel), + %% Nothing is drawn until wxPaintDC is destroyed. + try draw(Id, DC, Panel, Paint, Data) + catch _:Err -> + io:format("Crash ~p ~p~n",[Err, erlang:get_stacktrace()]) + end, + wxPaintDC:destroy(DC), + ok. +%%%%%%%%%% +handle_call(Event, From, _State) -> + error({unhandled_call, Event, From}). + +handle_cast(Event, _State) -> + error({unhandled_cast, Event}). +%%%%%%%%%% +handle_info(Stats = {stats, 1, _, _, _}, State = #state{panel=Panel, data=Data}) -> + wxWindow:refresh(Panel), + {noreply, State#state{data = add_data(Stats, Data)}}; + +handle_info({active, Node}, State = #state{parent=Parent, appmon=Old}) -> + create_menus(Parent, []), + try + Node = node(Old), + {noreply, State} + catch _:_ -> + catch Old ! exit, + Me = self(), + Pid = spawn_link(Node, fun() -> fetch_stats(Me) end), + {noreply, State#state{appmon=Pid, data={0, queue:new()}}} + end; + +handle_info(not_active, State = #state{appmon=_Pid}) -> + %% Pid ! exit, + {noreply, State}; + +handle_info(_Event, State) -> + io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]), + {noreply, State}. + +%%%%%%%%%% +terminate(_Event, #state{appmon=Pid}) -> + Pid ! exit, + ok. +code_change(_, _, State) -> + State. + +add_data(Stats, {N, Q}) when N > 60 -> + {N, queue:drop(queue:in(Stats, Q))}; +add_data(Stats, {N, Q}) -> + {N+1, queue:in(Stats, Q)}. + +fetch_stats(Parent) -> + receive + exit -> normal + after 1000 -> + M = Parent ! {stats, 1, + erlang:statistics(run_queues), + erlang:statistics(io), + erlang:memory()}, + %% io:format("IO ~p~n",[element(4,M)]), + fetch_stats(Parent) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_menus(Parent, _) -> + MenuEntries = + [{"File", + [ + ]} + ], + observer_wx:create_menus(Parent, MenuEntries). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +collect_data(?RQ_W, {N, Q}) -> + Data = [RQ || {stats, _Ver, RQ, _IO, _Mem} <- queue:to_list(Q)], + {N, lmax(Data), Data}; +collect_data(?MEM_W, {N, Q}) -> + Data = [{Mem} || {stats, _Ver, _RQ, _IO, Mem} <- queue:to_list(Q)], + {N, {bytes, lmax(Data)}, Data}; +collect_data(?IO_W, {N, Q}) -> + case queue:to_list(Q) of + [] -> {0, 0, []}; + [_] -> {0, 0, []}; + [{stats, _Ver, _RQ, {{_,In0}, {_,Out0}}, _Mem}|Data0] -> + [_,_|Data=[First|_]] = + lists:foldl(fun({stats, _, _, {{_,In}, {_,Out}}, _}, [PIn,Pout|Acc]) -> + [In,Out,{In-PIn,Out-Pout}|Acc] + end, [In0,Out0], Data0), + {N, {bytes, lmax(Data)}, lists:reverse([First|Data])} + end. + +lmax([]) -> 0; +lmax(List) -> + lists:max([lists:max(tuple_to_list(T)) || T <- List]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +draw(Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> + {Len, Max, Hs} = collect_data(Id, Data), + Size = wxWindow:getClientSize(Panel), + {X0,Y0,WS,HS} = draw_borders(Id, DC, Size, Max, Paint), + Start = max(61-Len, 0)*WS+X0, + case Hs of + [] -> ignore; + _ -> + Draw = fun(N) -> + Lines = make_lines(Hs, Start, N, Y0, WS, HS), + wxDC:setPen(DC, element(1+(N rem size(Pens)), Pens)), + wxDC:drawLines(DC, Lines), + N+1 + end, + [Draw(I) || I <- lists:seq(1,tuple_size(hd(Hs)))] + end, + ok. + +make_lines(Ds = [Data|_], PX, N, PY, WS, HS) -> + Y = element(N,Data), + make_lines(Ds, PX, N, PY, WS, HS, Y, []). + +make_lines([D1 | Ds = [D2|Rest]], PX, N, PY, WS, HS, Y0, Acc0) -> + Y1 = element(N,D1), + Y2 = element(N,D2), + Y3 = case Rest of + [D3|_] -> element(N,D3); + [] -> Y2 + end, + Acc = make_splines(Y0,Y1,Y2,Y3,PX,PY,WS,HS,[{round(PX),PY-round(Y1*HS)}|Acc0]), + make_lines(Ds, PX+WS, N, PY, WS, HS, Y1, Acc); +make_lines([D1], PX, N, PY, _WS, HS, _Y0, Acc) -> + Y1 = element(N,D1), + [{round(PX),PY-round(Y1*HS)}|Acc]. + +make_splines(_Y0,Y1,Y2,_Y3, _PX,_PY, _WS,HS, Acc) + when (abs(Y1-Y2) * HS) < 3.0 -> + Acc; +make_splines(_Y0,_Y1,_Y2,_Y3, _PX,_PY, WS,_HS, Acc) + when WS < 3.0 -> + Acc; +make_splines(Y00,Y10,Y20,Y30,PX,PY,WS,HS,Acc) -> + Y1 = Y10*HS, + Y2 = Y20*HS, + Steps = round(min(abs(Y1-Y2), WS)), + if Steps > 2 -> + Y0 = Y00*HS, + Y3 = Y30*HS, + Tan = spline_tan(Y0,Y1,Y2,Y3), + Delta = 1/Steps, + splines(Steps-1, 0.0, Delta, Tan, Y1,Y2, PX, PY, Delta*WS, Acc); + true -> + Acc + end. + +spline_tan(Y0, Y1, Y2, Y3) -> + S = 1.0, + C = 0.5, + %% Calc tangent values + M1 = S*C*(Y2-Y0), + M2 = S*C*(Y3-Y1), + {M1,M2}. + +splines(N, XD, XD0, Tan, Y1,Y2, PX, PY, WS, Acc) when N > 0 -> + Delta = XD+XD0, + Y = spline(Delta, Tan, Y1,Y2), + X = PX+WS, + %% io:format("Y1:~p Y(~p):~p Y2:~p~n",[round(Y1),round(X),round(Y),round(Y2)]), + splines(N-1, Delta, XD0, Tan, Y1, Y2, X, PY, WS, [{round(X),PY-round(Y)}|Acc]); +splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _PY, _WS, Acc) -> Acc. + +spline(T, {M1, M2}, Y1, Y2) -> + %% Hermite Basis Funcs + T2 = T*T, T3 = T*T*T, + H1 = 2*T3-3*T2+1, + H2 = -2*T3+3*T2, + H3 = T3-2*T2+T, + H4 = T3-T2, + %% Result + M1*H3 + Y1*H1 + Y2*H2 + M2*H4. + +-define(BW, 5). +-define(BH, 5). + +draw_borders(Type, DC, {W,H}, Max0, #paint{pen=Pen, font=Font}) -> + Max = calc_max(Max0), + wxDC:setPen(DC, Pen), + wxDC:setFont(DC, Font), + Str1 = observer_lib:to_str(Max), + Str2 = observer_lib:to_str(div2(Max)), + Str3 = observer_lib:to_str(0), + {TW,TH} = wxDC:getTextExtent(DC, Str1), + + GraphX0 = ?BW+TW+?BW, + GraphX1 = W-?BW*4, + TopTextX = ?BW+TW+?BW, + MaxTextY = ?BH+TH+?BH, + BottomTextY = H-?BH-TH, + SecondsY = BottomTextY - ?BH - TH, + GraphY0 = MaxTextY + (TH div 2), + GraphY1 = SecondsY - ?BH, + GraphW = max(GraphX1-GraphX0-1, 60), + GraphH = max(GraphY1-GraphY0-1, 100), + ScaleW = GraphW / 60, + ScaleH = calc_scale(GraphH, Max), + + case Type of + ?RQ_W -> wxDC:drawText(DC, "CPU History - Run queue length", {TopTextX,?BH}); + ?MEM_W -> wxDC:drawText(DC, "Memory Usage", {TopTextX,?BH}); + ?IO_W -> wxDC:drawText(DC, "IO Usage", {TopTextX,?BH}) + end, + + Align = fun(Str, Y) -> + {StrW, _} = wxDC:getTextExtent(DC, Str), + wxDC:drawText(DC, Str, {GraphX0 - StrW - ?BW, Y}) + end, + Align(Str1, MaxTextY), + Align(Str2, MaxTextY - (TW div 2) + (GraphY1 - MaxTextY) div 2), + Align(Str3, GraphY1 - (TH div 2) + 1), + + DrawSecs = fun(Secs, Pos) -> + Str = [observer_lib:to_str(Secs)|" s"], + wxDC:drawText(DC, Str, {round(GraphX0-?BH+Pos), SecondsY}), + Pos + 10*ScaleW + end, + lists:foldl(DrawSecs, 0, lists:seq(60,0, -10)), + case Type of + ?RQ_W -> wxDC:drawText(DC, "Scheduler", {?BW, BottomTextY}); + ?MEM_W -> wxDC:drawText(DC, "Memory", {?BW, BottomTextY}); + ?IO_W -> wxDC:drawText(DC, "Input Output", {?BW, BottomTextY}) + end, + + wxDC:drawLines(DC, [{GraphX0, GraphY0}, {GraphX0, GraphY1}, + {GraphX1, GraphY1}, {GraphX1, GraphY0}, {GraphX0, GraphY0}]), + {GraphX0, GraphY1, ScaleW, ScaleH}. + +div2({Type, Int}) -> {Type, Int div 2}; +div2(Int) -> Int div 2. + +calc_max(Max) when Max < 10 -> 10; +calc_max({Type, Max}) -> {Type,calc_max1(Max)}; +calc_max(Max) -> calc_max1(Max). + +calc_max1(Max) -> + case Max div 10 of + X when X < 10 -> + (X+1)*10; + X -> + 10*calc_max1(X) + end. + +calc_scale(H, {_Type, Max}) -> calc_scale(H,Max); +calc_scale(Height, Max) when Height > Max -> + Height / Max; +calc_scale(Height, Max) -> + Height / Max. diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 5a593abf11..2403b984e5 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -55,6 +55,7 @@ sys_panel, trace_panel, app_panel, + perf_panel, active_tab, node, nodes @@ -129,6 +130,10 @@ setup(#state{frame = Frame} = State) -> %% I postpone the creation of the other tabs so they can query/use %% the window size + %% Perf Viewer Panel + PerfPanel = observer_perf_wx:start_link(Notebook, self()), + wxNotebook:addPage(Notebook, PerfPanel, "Load Charts", []), + %% App Viewer Panel AppPanel = observer_app_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, AppPanel, "Applications", []), @@ -160,6 +165,7 @@ setup(#state{frame = Frame} = State) -> tv_panel = TVPanel, trace_panel = TracePanel, app_panel = AppPanel, + perf_panel = PerfPanel, active_tab = SysPid, node = node(), nodes = Nodes @@ -404,12 +410,15 @@ check_page_title(Notebook) -> wxNotebook:getPageText(Notebook, Selection). get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, - tv_panel=Tv, trace_panel=Trace, app_panel=App}) -> + tv_panel=Tv, trace_panel=Trace, app_panel=App, + perf_panel=Perf + }) -> Panel = case check_page_title(Notebook) of "Processes" -> Pro; "System" -> Sys; "Table Viewer" -> Tv; ?TRACE_STR -> Trace; + "Load Charts" -> Perf; "Applications" -> App end, wx_object:get_pid(Panel). -- cgit v1.2.3 From de67c104757f25794d93dc1c7d61d6d11e30e77a Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 10 Jan 2012 08:47:43 +0100 Subject: [observer] Add performance monitor --- lib/observer/src/observer_lib.erl | 2 + lib/observer/src/observer_perf_wx.erl | 206 +++++++++++++++++++++++----------- 2 files changed, 141 insertions(+), 67 deletions(-) diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index 5260861497..3b924d46cf 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -154,7 +154,9 @@ to_str(Value) when is_atom(Value) -> to_str({bytes, B}) -> KB = B div 1024, MB = KB div 1024, + GB = MB div 1024, if + GB > 10 -> integer_to_list(GB) ++ " gB"; MB > 10 -> integer_to_list(MB) ++ " mB"; KB > 0 -> integer_to_list(KB) ++ " kB"; true -> integer_to_list(B) ++ " B " diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 04e09f18d8..1f872db3a9 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -31,6 +31,8 @@ -record(state, { + offset = 0.0, + active = false, parent, windows, data = {0, queue:new()}, @@ -77,9 +79,7 @@ init([Notebook, Parent]) -> % wxPanel:connect(DrawingArea, size, [{skip, true}]), DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), - Cols = [{220, 50, 50}, {220, 50, 220}, {50, 50, 220}, - {50, 220, 220}, {50, 220, 50}, {220, 220, 50}], - Pens = [wxPen:new(Col) || Col <- Cols], + Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], %% GC = wxGraphicsContext:create(DrawingArea), %% _Font = wxGraphicsContext:createFont(GC, DefFont), {Panel, #state{parent=Parent, @@ -97,11 +97,6 @@ init([Notebook, Parent]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% handle_event(#wx{id=Id, event=_Sz=#wxSize{size=Size}}, -%% State=#state{}) -> -%% %% Id =:= ?DRAWAREA andalso setup_scrollbar(Size,AppWin,App), -%% {noreply, State}; - handle_event(#wx{event=#wxCommand{type=command_menu_selected}}, State = #state{}) -> {noreply, State}; @@ -111,12 +106,12 @@ handle_event(Event, _State) -> %%%%%%%%%% handle_sync_event(#wx{id=Id, event = #wxPaint{}},_, - #state{paint=Paint, windows=Windows, data=Data}) -> + #state{offset=Offset, paint=Paint, windows=Windows, data=Data}) -> %% PaintDC must be created in a callback to work on windows. Panel = element(Id, Windows), DC = wxPaintDC:new(Panel), %% Nothing is drawn until wxPaintDC is destroyed. - try draw(Id, DC, Panel, Paint, Data) + try draw(Offset, Id, DC, Panel, Paint, Data) catch _:Err -> io:format("Crash ~p ~p~n",[Err, erlang:get_stacktrace()]) end, @@ -129,25 +124,44 @@ handle_call(Event, From, _State) -> handle_cast(Event, _State) -> error({unhandled_cast, Event}). %%%%%%%%%% -handle_info(Stats = {stats, 1, _, _, _}, State = #state{panel=Panel, data=Data}) -> +handle_info(Stats = {stats, 1, _, _, _}, + State = #state{panel=Panel, data=Data, active=Active}) -> + if Active -> + wxWindow:refresh(Panel), + Freq = 6, + erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq}); + true -> ignore + end, + {noreply, State#state{offset=0.0, data = add_data(Stats, Data)}}; + +handle_info({refresh, Seq, Freq}, State = #state{panel=Panel, offset=Prev}) -> wxWindow:refresh(Panel), - {noreply, State#state{data = add_data(Stats, Data)}}; + Next = Seq+1, + if Seq > 1, Prev =:= 0.0 -> + %% We didn't have time to handle the refresh + {noreply, State}; + Next < Freq -> + erlang:send_after(trunc(1000 / Freq), self(), {refresh, Next, Freq}), + {noreply, State#state{offset=Seq/Freq}}; + true -> + {noreply, State#state{offset=Seq/Freq}} + end; handle_info({active, Node}, State = #state{parent=Parent, appmon=Old}) -> create_menus(Parent, []), try Node = node(Old), - {noreply, State} + {noreply, State#state{active=true}} catch _:_ -> catch Old ! exit, Me = self(), Pid = spawn_link(Node, fun() -> fetch_stats(Me) end), - {noreply, State#state{appmon=Pid, data={0, queue:new()}}} + {noreply, State#state{active=true, appmon=Pid, data={0, queue:new()}}} end; handle_info(not_active, State = #state{appmon=_Pid}) -> %% Pid ! exit, - {noreply, State}; + {noreply, State#state{active=false}}; handle_info(_Event, State) -> io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]), @@ -169,11 +183,11 @@ fetch_stats(Parent) -> receive exit -> normal after 1000 -> - M = Parent ! {stats, 1, - erlang:statistics(run_queues), - erlang:statistics(io), - erlang:memory()}, - %% io:format("IO ~p~n",[element(4,M)]), + _M = Parent ! {stats, 1, + erlang:statistics(run_queues), + erlang:statistics(io), + erlang:memory()}, + %% io:format("IO ~p~n",[element(4,_M)]), fetch_stats(Parent) end. @@ -193,8 +207,11 @@ collect_data(?RQ_W, {N, Q}) -> Data = [RQ || {stats, _Ver, RQ, _IO, _Mem} <- queue:to_list(Q)], {N, lmax(Data), Data}; collect_data(?MEM_W, {N, Q}) -> - Data = [{Mem} || {stats, _Ver, _RQ, _IO, Mem} <- queue:to_list(Q)], - {N, {bytes, lmax(Data)}, Data}; + MemT = mem_types(), + Data = [list_to_tuple([Value || {Type,Value} <- MemInfo, + lists:member(Type, MemT)]) + || {stats, _Ver, _RQ, _IO, MemInfo} <- queue:to_list(Q)], + {N, lmax(Data), Data}; collect_data(?IO_W, {N, Q}) -> case queue:to_list(Q) of [] -> {0, 0, []}; @@ -204,56 +221,61 @@ collect_data(?IO_W, {N, Q}) -> lists:foldl(fun({stats, _, _, {{_,In}, {_,Out}}, _}, [PIn,Pout|Acc]) -> [In,Out,{In-PIn,Out-Pout}|Acc] end, [In0,Out0], Data0), - {N, {bytes, lmax(Data)}, lists:reverse([First|Data])} + {N, lmax(Data), lists:reverse([First|Data])} end. +mem_types() -> + [total, processes, system, atom, binary, code, ets]. + lmax([]) -> 0; lmax(List) -> lists:max([lists:max(tuple_to_list(T)) || T <- List]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -draw(Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> +draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> {Len, Max, Hs} = collect_data(Id, Data), + NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, Size = wxWindow:getClientSize(Panel), - {X0,Y0,WS,HS} = draw_borders(Id, DC, Size, Max, Paint), - Start = max(61-Len, 0)*WS+X0, + {X0,Y0,WS,HS} = draw_borders(Id, NoGraphs, DC, Size, Max, Paint), + Start = max(61-Len, 0)*WS+X0 - Offset*WS, case Hs of [] -> ignore; _ -> Draw = fun(N) -> - Lines = make_lines(Hs, Start, N, Y0, WS, HS), - wxDC:setPen(DC, element(1+(N rem size(Pens)), Pens)), + Lines = make_lines(Hs, Start, N, X0, Y0, WS, HS), + wxDC:setPen(DC, element(1+ (N-1 rem tuple_size(Pens)) , Pens)), wxDC:drawLines(DC, Lines), N+1 end, - [Draw(I) || I <- lists:seq(1,tuple_size(hd(Hs)))] + [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)] end, ok. -make_lines(Ds = [Data|_], PX, N, PY, WS, HS) -> +make_lines(Ds = [Data|_], PX, N, MinX, MaxY, WS, HS) -> Y = element(N,Data), - make_lines(Ds, PX, N, PY, WS, HS, Y, []). + make_lines(Ds, PX, N, MinX, MaxY, WS, HS, Y, []). -make_lines([D1 | Ds = [D2|Rest]], PX, N, PY, WS, HS, Y0, Acc0) -> +make_lines([D1 | Ds = [D2|Rest]], PX, N, MinX, MaxY, WS, HS, Y0, Acc0) -> Y1 = element(N,D1), Y2 = element(N,D2), Y3 = case Rest of [D3|_] -> element(N,D3); [] -> Y2 end, - Acc = make_splines(Y0,Y1,Y2,Y3,PX,PY,WS,HS,[{round(PX),PY-round(Y1*HS)}|Acc0]), - make_lines(Ds, PX+WS, N, PY, WS, HS, Y1, Acc); -make_lines([D1], PX, N, PY, _WS, HS, _Y0, Acc) -> + This = {max(MinX, round(PX)),MaxY-round(Y1*HS)}, + Acc = make_splines(Y0,Y1,Y2,Y3,PX,MinX,MaxY,WS,HS,[This|Acc0]), + make_lines(Ds, PX+WS, N, MinX, MaxY, WS, HS, Y1, Acc); +make_lines([D1], PX, N, _MinX, MaxY, _WS, HS, _Y0, Acc) -> Y1 = element(N,D1), - [{round(PX),PY-round(Y1*HS)}|Acc]. + [{round(PX),MaxY-round(Y1*HS)}|Acc]. -make_splines(_Y0,Y1,Y2,_Y3, _PX,_PY, _WS,HS, Acc) +make_splines(_Y0,Y1,Y2,_Y3, _PX, _MinX,_MaxY, _WS,HS, Acc) when (abs(Y1-Y2) * HS) < 3.0 -> Acc; -make_splines(_Y0,_Y1,_Y2,_Y3, _PX,_PY, WS,_HS, Acc) +make_splines(_Y0,_Y1,_Y2,_Y3, _PX, _MinX,_MaxY, WS,_HS, Acc) when WS < 3.0 -> Acc; -make_splines(Y00,Y10,Y20,Y30,PX,PY,WS,HS,Acc) -> +make_splines(Y00,Y10,Y20,Y30,PX,MinX,MaxY,WS,HS,Acc) -> Y1 = Y10*HS, Y2 = Y20*HS, Steps = round(min(abs(Y1-Y2), WS)), @@ -262,26 +284,22 @@ make_splines(Y00,Y10,Y20,Y30,PX,PY,WS,HS,Acc) -> Y3 = Y30*HS, Tan = spline_tan(Y0,Y1,Y2,Y3), Delta = 1/Steps, - splines(Steps-1, 0.0, Delta, Tan, Y1,Y2, PX, PY, Delta*WS, Acc); + splines(Steps-1, 0.0, Delta, Tan, Y1,Y2, PX, MinX,MaxY, Delta*WS, Acc); true -> Acc end. -spline_tan(Y0, Y1, Y2, Y3) -> - S = 1.0, - C = 0.5, - %% Calc tangent values - M1 = S*C*(Y2-Y0), - M2 = S*C*(Y3-Y1), - {M1,M2}. - -splines(N, XD, XD0, Tan, Y1,Y2, PX, PY, WS, Acc) when N > 0 -> +splines(N, XD, XD0, Tan, Y1,Y2, PX0, MinX,MaxY, WS, Acc) when N > 0 -> + PX = PX0+WS, Delta = XD+XD0, - Y = spline(Delta, Tan, Y1,Y2), - X = PX+WS, - %% io:format("Y1:~p Y(~p):~p Y2:~p~n",[round(Y1),round(X),round(Y),round(Y2)]), - splines(N-1, Delta, XD0, Tan, Y1, Y2, X, PY, WS, [{round(X),PY-round(Y)}|Acc]); -splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _PY, _WS, Acc) -> Acc. + if PX < MinX -> + splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, MinX,MaxY, WS, Acc); + true -> + Y = max(0,round(spline(Delta, Tan, Y1,Y2))), + %% io:format("Y1:~p Y(~p):~p Y2:~p~n",[round(Y1),round(X),round(Y),round(Y2)]), + splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, MinX,MaxY, WS, [{round(PX), MaxY-Y}|Acc]) + end; +splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _MinX,_MaxY, _WS, Acc) -> Acc. spline(T, {M1, M2}, Y1, Y2) -> %% Hermite Basis Funcs @@ -293,15 +311,25 @@ spline(T, {M1, M2}, Y1, Y2) -> %% Result M1*H3 + Y1*H1 + Y2*H2 + M2*H4. +spline_tan(Y0, Y1, Y2, Y3) -> + S = 1.0, + C = 0.5, + %% Calc tangent values + M1 = S*C*(Y2-Y0), + M2 = S*C*(Y3-Y1), + {M1,M2}. + -define(BW, 5). -define(BH, 5). -draw_borders(Type, DC, {W,H}, Max0, #paint{pen=Pen, font=Font}) -> +draw_borders(Type, NoGraphs, DC, {W,H}, Max0, + #paint{pen=Pen, font=Font}) -> Max = calc_max(Max0), wxDC:setPen(DC, Pen), wxDC:setFont(DC, Font), - Str1 = observer_lib:to_str(Max), - Str2 = observer_lib:to_str(div2(Max)), + {Unit, MaxUnit} = bytes(Type, Max), + Str1 = observer_lib:to_str(MaxUnit), + Str2 = observer_lib:to_str(MaxUnit div 2), Str3 = observer_lib:to_str(0), {TW,TH} = wxDC:getTextExtent(DC, Str1), @@ -320,8 +348,8 @@ draw_borders(Type, DC, {W,H}, Max0, #paint{pen=Pen, font=Font}) -> case Type of ?RQ_W -> wxDC:drawText(DC, "CPU History - Run queue length", {TopTextX,?BH}); - ?MEM_W -> wxDC:drawText(DC, "Memory Usage", {TopTextX,?BH}); - ?IO_W -> wxDC:drawText(DC, "IO Usage", {TopTextX,?BH}) + ?MEM_W -> wxDC:drawText(DC, "Memory Usage " ++ Unit, {TopTextX,?BH}); + ?IO_W -> wxDC:drawText(DC, "IO Usage " ++ Unit, {TopTextX,?BH}) end, Align = fun(Str, Y) -> @@ -338,21 +366,46 @@ draw_borders(Type, DC, {W,H}, Max0, #paint{pen=Pen, font=Font}) -> Pos + 10*ScaleW end, lists:foldl(DrawSecs, 0, lists:seq(60,0, -10)), - case Type of - ?RQ_W -> wxDC:drawText(DC, "Scheduler", {?BW, BottomTextY}); - ?MEM_W -> wxDC:drawText(DC, "Memory", {?BW, BottomTextY}); - ?IO_W -> wxDC:drawText(DC, "Input Output", {?BW, BottomTextY}) - end, wxDC:drawLines(DC, [{GraphX0, GraphY0}, {GraphX0, GraphY1}, {GraphX1, GraphY1}, {GraphX1, GraphY0}, {GraphX0, GraphY0}]), - {GraphX0, GraphY1, ScaleW, ScaleH}. + + {SpaceW, _} = wxDC:getTextExtent(DC, " "), + Text = fun(X,Y, Str, PenId) -> + if PenId == 0 -> + wxDC:setTextForeground(DC, {0,0,0}); + PenId > 0 -> + wxDC:setTextForeground(DC, element(PenId, colors())) + end, + wxDC:drawText(DC, Str, {X, Y}), + {StrW, _} = wxDC:getTextExtent(DC, Str), + StrW + X + SpaceW + end, + case Type of + ?RQ_W -> + TN0 = Text(?BW, BottomTextY, "Scheduler: ", 0), + lists:foldl(fun(Id, Pos0) -> + Text(Pos0, BottomTextY, integer_to_list(Id), Id) + end, TN0, lists:seq(1, NoGraphs)); + ?MEM_W -> + lists:foldl(fun(MType, {PenId, Pos0}) -> + Str = uppercase(atom_to_list(MType)), + Pos = Text(Pos0, BottomTextY, Str, PenId), + {PenId+1, Pos} + end, {1, ?BW}, mem_types()); + ?IO_W -> + TN0 = Text(?BW, BottomTextY, "Input", 1), + Text(TN0, BottomTextY, "Output", 2) + end, + {GraphX0+1, GraphY1, ScaleW, ScaleH}. div2({Type, Int}) -> {Type, Int div 2}; div2(Int) -> Int div 2. +uppercase([C|Rest]) -> + [C-$a+$A|Rest]. + calc_max(Max) when Max < 10 -> 10; -calc_max({Type, Max}) -> {Type,calc_max1(Max)}; calc_max(Max) -> calc_max1(Max). calc_max1(Max) -> @@ -363,8 +416,27 @@ calc_max1(Max) -> 10*calc_max1(X) end. +bytes(?RQ_W, Val) -> {"", Val}; +bytes(_, B) -> + KB = B div 1024, + MB = KB div 1024, + GB = MB div 1024, + if + GB > 10 -> {"(GB)", GB}; + MB > 10 -> {"(MB)", MB}; + KB > 0 -> {"(KB)", KB}; + true -> {"(B)", B} + end. + calc_scale(H, {_Type, Max}) -> calc_scale(H,Max); calc_scale(Height, Max) when Height > Max -> Height / Max; calc_scale(Height, Max) -> Height / Max. + +colors() -> + {{220, 50, 50}, {50, 220, 50}, {50, 50, 220}, + {220, 220, 50}, {50, 220, 220}, {220, 50, 220}, + {220, 100, 100}, {220, 100, 220}, + {100, 220, 220}, {100, 220, 100} + }. -- cgit v1.2.3 From 052a42cd072a2ac823d2c5f2cf5436ec4f391f34 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 19 Jan 2012 15:22:47 +0100 Subject: [observer] Use new scheduler_wall_time measurment --- lib/observer/src/observer_perf_wx.erl | 167 +++++++++++++++++++++------------- lib/observer/src/observer_wx.erl | 22 ++++- 2 files changed, 123 insertions(+), 66 deletions(-) diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 1f872db3a9..0d18112085 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -23,11 +23,15 @@ -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_sync_event/3, handle_cast/2]). +-export([fetch_stats/2]). + +-compile(export_all). + -behaviour(wx_object). -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). --compile(export_all). +%%-compile(export_all). -record(state, { @@ -41,7 +45,7 @@ appmon }). --record(paint, {font, pen, pens}). +-record(paint, {font, small, pen, pen2, pens}). -define(RQ_W, 1). -define(MEM_W, 2). @@ -53,7 +57,6 @@ start_link(Notebook, Parent) -> init([Notebook, Parent]) -> try Panel = wxPanel:new(Notebook), - %% wxWindow:setBackgroundColour(Panel, {222,222,222}), Main = wxBoxSizer:new(?wxVERTICAL), CPU = wxPanel:new(Panel, [{winid, ?RQ_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), @@ -76,17 +79,22 @@ init([Notebook, Parent]) -> wxPanel:connect(CPU, paint, [callback]), wxPanel:connect(IO, paint, [callback]), wxPanel:connect(MEM, paint, [callback]), - % wxPanel:connect(DrawingArea, size, [{skip, true}]), DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + DefSize = wxFont:getPointSize(DefFont), + DefFamily = wxFont:getFamily(DefFont), + Font = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), + SmallFont = wxFont:new(10, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + BlackPen = wxPen:new({0,0,0}, [{width, 2}]), Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], - %% GC = wxGraphicsContext:create(DrawingArea), - %% _Font = wxGraphicsContext:createFont(GC, DefFont), + process_flag(trap_exit, true), {Panel, #state{parent=Parent, panel =Panel, windows = {CPU, MEM, IO}, - paint=#paint{font= DefFont, - pen = ?wxBLACK_PEN, + paint=#paint{font = Font, + small = SmallFont, + pen = ?wxGREY_PEN, + pen2 = BlackPen, pens = list_to_tuple(Pens) } }} @@ -155,7 +163,7 @@ handle_info({active, Node}, State = #state{parent=Parent, appmon=Old}) -> catch _:_ -> catch Old ! exit, Me = self(), - Pid = spawn_link(Node, fun() -> fetch_stats(Me) end), + Pid = spawn_link(Node, ?MODULE, fetch_stats, [Me, 1000]), {noreply, State#state{active=true, appmon=Pid, data={0, queue:new()}}} end; @@ -163,13 +171,16 @@ handle_info(not_active, State = #state{appmon=_Pid}) -> %% Pid ! exit, {noreply, State#state{active=false}}; +handle_info({'EXIT', Old, _}, State = #state{appmon=Old}) -> + {noreply, State#state{active=false, appmon=undefined}}; + handle_info(_Event, State) -> io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]), {noreply, State}. %%%%%%%%%% terminate(_Event, #state{appmon=Pid}) -> - Pid ! exit, + catch Pid ! exit, ok. code_change(_, _, State) -> State. @@ -179,16 +190,21 @@ add_data(Stats, {N, Q}) when N > 60 -> add_data(Stats, {N, Q}) -> {N+1, queue:in(Stats, Q)}. -fetch_stats(Parent) -> +fetch_stats(Parent, Time) -> + erlang:system_flag(scheduler_wall_time, true), + fetch_stats_loop(Parent, Time), + erlang:system_flag(scheduler_wall_time, false). + +fetch_stats_loop(Parent, Time) -> receive exit -> normal - after 1000 -> + after Time -> _M = Parent ! {stats, 1, - erlang:statistics(run_queues), + erlang:statistics(scheduler_wall_time), erlang:statistics(io), erlang:memory()}, %% io:format("IO ~p~n",[element(4,_M)]), - fetch_stats(Parent) + fetch_stats(Parent, Time) end. @@ -204,8 +220,18 @@ create_menus(Parent, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% collect_data(?RQ_W, {N, Q}) -> - Data = [RQ || {stats, _Ver, RQ, _IO, _Mem} <- queue:to_list(Q)], - {N, lmax(Data), Data}; + case queue:to_list(Q) of + [] -> {0, 0, []}; + [_] -> {0, 0, []}; + [{stats, _Ver, Init0, _IO, _Mem}|Data0] -> + Init = lists:sort(Init0), + [_|Data=[First|_]] = lists:foldl(fun({stats, _, T0, _, _}, [Prev|Acc]) -> + TN = lists:sort(T0), + Delta = calc_delta(TN, Prev), + [TN, list_to_tuple(Delta)|Acc] + end, [Init], Data0), + {N, lmax(Data), lists:reverse([First|Data])} + end; collect_data(?MEM_W, {N, Q}) -> MemT = mem_types(), Data = [list_to_tuple([Value || {Type,Value} <- MemInfo, @@ -231,6 +257,10 @@ lmax([]) -> 0; lmax(List) -> lists:max([lists:max(tuple_to_list(T)) || T <- List]). +calc_delta([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) -> + [100*(WN-WP) div (TN-TP)|calc_delta(Ss, Ps)]; +calc_delta([], []) -> []. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> {Len, Max, Hs} = collect_data(Id, Data), @@ -242,7 +272,7 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> [] -> ignore; _ -> Draw = fun(N) -> - Lines = make_lines(Hs, Start, N, X0, Y0, WS, HS), + Lines = make_lines(Hs, Start, N, {X0,round(Max*HS)}, Y0, WS, HS), wxDC:setPen(DC, element(1+ (N-1 rem tuple_size(Pens)) , Pens)), wxDC:drawLines(DC, Lines), N+1 @@ -251,31 +281,31 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> end, ok. -make_lines(Ds = [Data|_], PX, N, MinX, MaxY, WS, HS) -> +make_lines(Ds = [Data|_], PX, N, Clip, ZeroY, WS, HS) -> Y = element(N,Data), - make_lines(Ds, PX, N, MinX, MaxY, WS, HS, Y, []). + make_lines(Ds, PX, N, Clip, ZeroY, WS, HS, Y, []). -make_lines([D1 | Ds = [D2|Rest]], PX, N, MinX, MaxY, WS, HS, Y0, Acc0) -> +make_lines([D1 | Ds = [D2|Rest]], PX, N, Clip={Cx,Cy}, ZeroY, WS, HS, Y0, Acc0) -> Y1 = element(N,D1), Y2 = element(N,D2), Y3 = case Rest of [D3|_] -> element(N,D3); [] -> Y2 end, - This = {max(MinX, round(PX)),MaxY-round(Y1*HS)}, - Acc = make_splines(Y0,Y1,Y2,Y3,PX,MinX,MaxY,WS,HS,[This|Acc0]), - make_lines(Ds, PX+WS, N, MinX, MaxY, WS, HS, Y1, Acc); -make_lines([D1], PX, N, _MinX, MaxY, _WS, HS, _Y0, Acc) -> + This = {max(Cx, round(PX)),ZeroY-min(Cy,round(Y1*HS))}, + Acc = make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,[This|Acc0]), + make_lines(Ds, PX+WS, N, Clip, ZeroY, WS, HS, Y1, Acc); +make_lines([D1], PX, N, _Clip, ZeroY, _WS, HS, _Y0, Acc) -> Y1 = element(N,D1), - [{round(PX),MaxY-round(Y1*HS)}|Acc]. + [{round(PX),ZeroY-round(Y1*HS)}|Acc]. -make_splines(_Y0,Y1,Y2,_Y3, _PX, _MinX,_MaxY, _WS,HS, Acc) +make_splines(_Y0,Y1,Y2,_Y3, _PX, _Clip,_ZeroY, _WS,HS, Acc) when (abs(Y1-Y2) * HS) < 3.0 -> Acc; -make_splines(_Y0,_Y1,_Y2,_Y3, _PX, _MinX,_MaxY, WS,_HS, Acc) +make_splines(_Y0,_Y1,_Y2,_Y3, _PX, _Clip,_ZeroY, WS,_HS, Acc) when WS < 3.0 -> Acc; -make_splines(Y00,Y10,Y20,Y30,PX,MinX,MaxY,WS,HS,Acc) -> +make_splines(Y00,Y10,Y20,Y30,PX,Clip,ZeroY,WS,HS,Acc) -> Y1 = Y10*HS, Y2 = Y20*HS, Steps = round(min(abs(Y1-Y2), WS)), @@ -284,22 +314,23 @@ make_splines(Y00,Y10,Y20,Y30,PX,MinX,MaxY,WS,HS,Acc) -> Y3 = Y30*HS, Tan = spline_tan(Y0,Y1,Y2,Y3), Delta = 1/Steps, - splines(Steps-1, 0.0, Delta, Tan, Y1,Y2, PX, MinX,MaxY, Delta*WS, Acc); + splines(Steps-1, 0.0, Delta, Tan, Y1,Y2, PX, Clip,ZeroY, Delta*WS, Acc); true -> Acc end. -splines(N, XD, XD0, Tan, Y1,Y2, PX0, MinX,MaxY, WS, Acc) when N > 0 -> +splines(N, XD, XD0, Tan, Y1,Y2, PX0, Clip={Cx,Cy},ZeroY, WS, Acc) when N > 0 -> PX = PX0+WS, Delta = XD+XD0, - if PX < MinX -> - splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, MinX,MaxY, WS, Acc); + if PX < Cx -> + splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, Acc); true -> - Y = max(0,round(spline(Delta, Tan, Y1,Y2))), + Y = min(Cy, max(0,round(spline(Delta, Tan, Y1,Y2)))), %% io:format("Y1:~p Y(~p):~p Y2:~p~n",[round(Y1),round(X),round(Y),round(Y2)]), - splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, MinX,MaxY, WS, [{round(PX), MaxY-Y}|Acc]) + splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, + [{round(PX), ZeroY-Y}|Acc]) end; -splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _MinX,_MaxY, _WS, Acc) -> Acc. +splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _Clip,_ZeroY, _WS, Acc) -> Acc. spline(T, {M1, M2}, Y1, Y2) -> %% Hermite Basis Funcs @@ -323,15 +354,14 @@ spline_tan(Y0, Y1, Y2, Y3) -> -define(BH, 5). draw_borders(Type, NoGraphs, DC, {W,H}, Max0, - #paint{pen=Pen, font=Font}) -> + #paint{pen=Pen, pen2=Pen2, font=Font, small=Small}) -> Max = calc_max(Max0), - wxDC:setPen(DC, Pen), - wxDC:setFont(DC, Font), {Unit, MaxUnit} = bytes(Type, Max), Str1 = observer_lib:to_str(MaxUnit), Str2 = observer_lib:to_str(MaxUnit div 2), Str3 = observer_lib:to_str(0), {TW,TH} = wxDC:getTextExtent(DC, Str1), + {SpaceW, _} = wxDC:getTextExtent(DC, " "), GraphX0 = ?BW+TW+?BW, GraphX1 = W-?BW*4, @@ -341,36 +371,49 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max0, SecondsY = BottomTextY - ?BH - TH, GraphY0 = MaxTextY + (TH div 2), GraphY1 = SecondsY - ?BH, - GraphW = max(GraphX1-GraphX0-1, 60), - GraphH = max(GraphY1-GraphY0-1, 100), + GraphW = GraphX1-GraphX0-1, + GraphH = GraphY1-GraphY0-1, + GraphY25 = GraphY0 + (GraphY1 - GraphY0) div 4, + GraphY50 = GraphY0 + (GraphY1 - GraphY0) div 2, + GraphY75 = GraphY0 + 3*(GraphY1 - GraphY0) div 4, ScaleW = GraphW / 60, - ScaleH = calc_scale(GraphH, Max), - - case Type of - ?RQ_W -> wxDC:drawText(DC, "CPU History - Run queue length", {TopTextX,?BH}); - ?MEM_W -> wxDC:drawText(DC, "Memory Usage " ++ Unit, {TopTextX,?BH}); - ?IO_W -> wxDC:drawText(DC, "IO Usage " ++ Unit, {TopTextX,?BH}) - end, + ScaleH = GraphH / Max, + wxDC:setFont(DC, Small), Align = fun(Str, Y) -> {StrW, _} = wxDC:getTextExtent(DC, Str), wxDC:drawText(DC, Str, {GraphX0 - StrW - ?BW, Y}) end, Align(Str1, MaxTextY), - Align(Str2, MaxTextY - (TW div 2) + (GraphY1 - MaxTextY) div 2), + Align(Str2, GraphY50 - (TH div 2)), Align(Str3, GraphY1 - (TH div 2) + 1), + wxDC:setPen(DC, Pen), DrawSecs = fun(Secs, Pos) -> Str = [observer_lib:to_str(Secs)|" s"], - wxDC:drawText(DC, Str, {round(GraphX0-?BH+Pos), SecondsY}), + X = round(GraphX0+Pos), + wxDC:drawText(DC, Str, {X-SpaceW, SecondsY}), + wxDC:drawLine(DC, {X, GraphY0}, {X, GraphY1+5}), Pos + 10*ScaleW end, lists:foldl(DrawSecs, 0, lists:seq(60,0, -10)), - wxDC:drawLines(DC, [{GraphX0, GraphY0}, {GraphX0, GraphY1}, - {GraphX1, GraphY1}, {GraphX1, GraphY0}, {GraphX0, GraphY0}]), + wxDC:drawLine(DC, {GraphX0-3, GraphY25}, {GraphX1, GraphY25}), + wxDC:drawLine(DC, {GraphX0-3, GraphY50}, {GraphX1, GraphY50}), + wxDC:drawLine(DC, {GraphX0-3, GraphY75}, {GraphX1, GraphY75}), + + wxDC:setPen(DC, Pen2), + wxDC:drawLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, + {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, + {GraphX0, GraphY0-1}]), + + wxDC:setFont(DC, Font), + case Type of + ?RQ_W -> wxDC:drawText(DC, "Scheduler Utilization (%) ", {TopTextX,?BH}); + ?MEM_W -> wxDC:drawText(DC, "Memory Usage " ++ Unit, {TopTextX,?BH}); + ?IO_W -> wxDC:drawText(DC, "IO Usage " ++ Unit, {TopTextX,?BH}) + end, - {SpaceW, _} = wxDC:getTextExtent(DC, " "), Text = fun(X,Y, Str, PenId) -> if PenId == 0 -> wxDC:setTextForeground(DC, {0,0,0}); @@ -411,7 +454,11 @@ calc_max(Max) -> calc_max1(Max). calc_max1(Max) -> case Max div 10 of X when X < 10 -> - (X+1)*10; + case Max rem 10 of + 0 -> Max; + _ -> + (X+1)*10 + end; X -> 10*calc_max1(X) end. @@ -428,15 +475,9 @@ bytes(_, B) -> true -> {"(B)", B} end. -calc_scale(H, {_Type, Max}) -> calc_scale(H,Max); -calc_scale(Height, Max) when Height > Max -> - Height / Max; -calc_scale(Height, Max) -> - Height / Max. - colors() -> - {{220, 50, 50}, {50, 220, 50}, {50, 50, 220}, - {220, 220, 50}, {50, 220, 220}, {220, 50, 220}, - {220, 100, 100}, {220, 100, 220}, - {100, 220, 220}, {100, 220, 100} + {{200, 50, 50}, {50, 200, 50}, {50, 50, 200}, + {255, 110, 0}, {50, 200, 200}, {200, 50, 200}, + {240, 200, 80}, {140, 2, 140}, + {100, 200, 240}, {100, 240, 100} }. diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 2403b984e5..e2b256d768 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -328,8 +328,9 @@ handle_info({nodedown, Node}, create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION), {noreply, State3}; -handle_info({'EXIT', _Pid, _Reason}, State) -> - io:format("Child crashed exiting: ~p ~p~n", [_Pid,_Reason]), +handle_info({'EXIT', Pid, _Reason}, State) -> + io:format("Child (~s) crashed exiting: ~p ~p~n", + [pid2panel(Pid, State), Pid,_Reason]), {stop, normal, State}; handle_info(_Info, State) -> @@ -351,6 +352,7 @@ try_rpc(Node, Mod, Func, Args) -> error_logger:error_report([{node, Node}, {call, {Mod, Func, Args}}, {reason, {badrpc, Reason}}]), + observer ! {nodedown, Node}, error({badrpc, Reason}); Res -> Res @@ -423,6 +425,20 @@ get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, end, wx_object:get_pid(Panel). +pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys, + tv_panel=Tv, trace_panel=Trace, app_panel=App, + perf_panel=Perf}) -> + case Pid of + Pro -> "Processes"; + Sys -> "System"; + Tv -> "Table Viewer" ; + Trace -> ?TRACE_STR; + Perf -> "Load Charts"; + App -> "Applications"; + _ -> "unknown" + end. + + create_connect_dialog(ping, #state{frame = Frame}) -> Dialog = wxTextEntryDialog:new(Frame, "Connect to node"), case wxDialog:showModal(Dialog) of -- cgit v1.2.3 From 49da7aaaed1f8f998bd3d6f1f029236fab9b3d4b Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 31 Jan 2012 16:13:23 +0100 Subject: [observer] Move data collector to run_time tools --- lib/observer/src/observer_perf_wx.erl | 36 ++++-------------------------- lib/runtime_tools/src/observer_backend.erl | 20 +++++++++++++++-- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 0d18112085..48ae74cce1 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -23,16 +23,10 @@ -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_sync_event/3, handle_cast/2]). --export([fetch_stats/2]). - --compile(export_all). - -behaviour(wx_object). -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). -%%-compile(export_all). - -record(state, { offset = 0.0, @@ -163,7 +157,7 @@ handle_info({active, Node}, State = #state{parent=Parent, appmon=Old}) -> catch _:_ -> catch Old ! exit, Me = self(), - Pid = spawn_link(Node, ?MODULE, fetch_stats, [Me, 1000]), + Pid = spawn_link(Node, observer_backend, fetch_stats, [Me, 1000]), {noreply, State#state{active=true, appmon=Pid, data={0, queue:new()}}} end; @@ -190,24 +184,6 @@ add_data(Stats, {N, Q}) when N > 60 -> add_data(Stats, {N, Q}) -> {N+1, queue:in(Stats, Q)}. -fetch_stats(Parent, Time) -> - erlang:system_flag(scheduler_wall_time, true), - fetch_stats_loop(Parent, Time), - erlang:system_flag(scheduler_wall_time, false). - -fetch_stats_loop(Parent, Time) -> - receive - exit -> normal - after Time -> - _M = Parent ! {stats, 1, - erlang:statistics(scheduler_wall_time), - erlang:statistics(io), - erlang:memory()}, - %% io:format("IO ~p~n",[element(4,_M)]), - fetch_stats(Parent, Time) - end. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% create_menus(Parent, _) -> @@ -263,7 +239,8 @@ calc_delta([], []) -> []. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> - {Len, Max, Hs} = collect_data(Id, Data), + {Len, Max0, Hs} = collect_data(Id, Data), + Max = calc_max(Max0), NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, Size = wxWindow:getClientSize(Panel), {X0,Y0,WS,HS} = draw_borders(Id, NoGraphs, DC, Size, Max, Paint), @@ -326,7 +303,6 @@ splines(N, XD, XD0, Tan, Y1,Y2, PX0, Clip={Cx,Cy},ZeroY, WS, Acc) when N > 0 -> splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, Acc); true -> Y = min(Cy, max(0,round(spline(Delta, Tan, Y1,Y2)))), - %% io:format("Y1:~p Y(~p):~p Y2:~p~n",[round(Y1),round(X),round(Y),round(Y2)]), splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, [{round(PX), ZeroY-Y}|Acc]) end; @@ -353,9 +329,8 @@ spline_tan(Y0, Y1, Y2, Y3) -> -define(BW, 5). -define(BH, 5). -draw_borders(Type, NoGraphs, DC, {W,H}, Max0, +draw_borders(Type, NoGraphs, DC, {W,H}, Max, #paint{pen=Pen, pen2=Pen2, font=Font, small=Small}) -> - Max = calc_max(Max0), {Unit, MaxUnit} = bytes(Type, Max), Str1 = observer_lib:to_str(MaxUnit), Str2 = observer_lib:to_str(MaxUnit div 2), @@ -442,9 +417,6 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max0, end, {GraphX0+1, GraphY1, ScaleW, ScaleH}. -div2({Type, Int}) -> {Type, Int div 2}; -div2(Int) -> Int div 2. - uppercase([C|Rest]) -> [C-$a+$A|Rest]. diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 2f8ffbcdb6..01e99f3f5e 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ -export([vsn/0]). %% observer stuff --export([sys_info/0, get_table/3, get_table_list/2]). +-export([sys_info/0, get_table/3, get_table_list/2, fetch_stats/2]). %% etop stuff -export([etop_collect/1]). @@ -198,6 +198,22 @@ get_table_list(mnesia, Opts) -> end, lists:foldl(Info, [], mnesia:system_info(tables)). +fetch_stats(Parent, Time) -> + erlang:system_flag(scheduler_wall_time, true), + process_flag(trap_exit, true), + fetch_stats_loop(Parent, Time), + erlang:system_flag(scheduler_wall_time, false). + +fetch_stats_loop(Parent, Time) -> + receive + _Msg -> normal + after Time -> + _M = Parent ! {stats, 1, + erlang:statistics(scheduler_wall_time), + erlang:statistics(io), + erlang:memory()}, + fetch_stats(Parent, Time) + end. %% %% etop backend %% -- cgit v1.2.3 From 12cce9d67fe2644a986820dcdbeecc34ff929b29 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 2 Feb 2012 11:02:09 +0100 Subject: [observer] Fix crash when loosing connections --- lib/observer/src/observer_app_wx.erl | 7 ++++++- lib/observer/src/observer_perf_wx.erl | 32 ++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 62046577ad..7b4f7c4ce5 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -106,6 +106,7 @@ init([Notebook, Parent]) -> LinkPen = wxPen:new(SelCol, [{width, 2}]), %% GC = wxGraphicsContext:create(DrawingArea), %% _Font = wxGraphicsContext:createFont(GC, DefFont), + process_flag(trap_exit, true), {Panel, #state{parent=Parent, panel =Panel, apps_w=Apps, @@ -269,6 +270,10 @@ handle_info({delivery, Pid, app, Curr, AppData}, wxWindow:layout(Panel), {noreply, State#state{app=App, sel=undefined}}; +handle_info({'EXIT', _, noconnection}, State) -> + {noreply, State}; +handle_info({'EXIT', _, normal}, State) -> + {noreply, State}; handle_info(_Event, State) -> %% io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]), {noreply, State}. diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 48ae74cce1..3b6befdf9b 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -108,14 +108,15 @@ handle_event(Event, _State) -> %%%%%%%%%% handle_sync_event(#wx{id=Id, event = #wxPaint{}},_, - #state{offset=Offset, paint=Paint, windows=Windows, data=Data}) -> + #state{active=Active, offset=Offset, paint=Paint, + windows=Windows, data=Data}) -> %% PaintDC must be created in a callback to work on windows. Panel = element(Id, Windows), DC = wxPaintDC:new(Panel), %% Nothing is drawn until wxPaintDC is destroyed. - try draw(Offset, Id, DC, Panel, Paint, Data) + try draw(Offset, Id, DC, Panel, Paint, Data, Active) catch _:Err -> - io:format("Crash ~p ~p~n",[Err, erlang:get_stacktrace()]) + io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()]) end, wxPaintDC:destroy(DC), ok. @@ -149,15 +150,17 @@ handle_info({refresh, Seq, Freq}, State = #state{panel=Panel, offset=Prev}) -> {noreply, State#state{offset=Seq/Freq}} end; -handle_info({active, Node}, State = #state{parent=Parent, appmon=Old}) -> +handle_info({active, Node}, State = #state{parent=Parent, panel=Panel, appmon=Old}) -> create_menus(Parent, []), try Node = node(Old), + wxWindow:refresh(Panel), {noreply, State#state{active=true}} catch _:_ -> catch Old ! exit, Me = self(), Pid = spawn_link(Node, observer_backend, fetch_stats, [Me, 1000]), + wxWindow:refresh(Panel), {noreply, State#state{active=true, appmon=Pid, data={0, queue:new()}}} end; @@ -169,7 +172,7 @@ handle_info({'EXIT', Old, _}, State = #state{appmon=Old}) -> {noreply, State#state{active=false, appmon=undefined}}; handle_info(_Event, State) -> - io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]), + %% io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]), {noreply, State}. %%%%%%%%%% @@ -238,7 +241,10 @@ calc_delta([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) -> calc_delta([], []) -> []. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> +draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) -> + %% This can be optimized a lot by collecting data once + %% and draw to memory and then blit memory and only draw new entries in new memory + %% area. {Len, Max0, Hs} = collect_data(Id, Data), Max = calc_max(Max0), NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, @@ -250,12 +256,21 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens}, Data) -> _ -> Draw = fun(N) -> Lines = make_lines(Hs, Start, N, {X0,round(Max*HS)}, Y0, WS, HS), - wxDC:setPen(DC, element(1+ (N-1 rem tuple_size(Pens)) , Pens)), + wxDC:setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), wxDC:drawLines(DC, Lines), N+1 end, [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)] end, + case Active of + false -> + NotActive = "Service not available", + wxDC:setTextForeground(DC, {0,0,0}), + wxDC:setFont(DC, Small), + wxDC:drawText(DC, NotActive, {X0 + 100, element(2,Size) div 2}); + true -> + ignore + end, ok. make_lines(Ds = [Data|_], PX, N, Clip, ZeroY, WS, HS) -> @@ -393,7 +408,8 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, if PenId == 0 -> wxDC:setTextForeground(DC, {0,0,0}); PenId > 0 -> - wxDC:setTextForeground(DC, element(PenId, colors())) + Id = 1 + ((PenId-1) rem tuple_size(colors())), + wxDC:setTextForeground(DC, element(Id, colors())) end, wxDC:drawText(DC, Str, {X, Y}), {StrW, _} = wxDC:getTextExtent(DC, Str), -- cgit v1.2.3 From dac51a6042e01081056899dc1d694f68e99be782 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 6 Feb 2012 16:02:28 +0100 Subject: [observer] Mac workaround --- lib/observer/src/observer_perf_wx.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 3b6befdf9b..8f4e317d99 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -107,14 +107,20 @@ handle_event(Event, _State) -> error({unhandled_event, Event}). %%%%%%%%%% -handle_sync_event(#wx{id=Id, event = #wxPaint{}},_, +handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, #state{active=Active, offset=Offset, paint=Paint, windows=Windows, data=Data}) -> %% PaintDC must be created in a callback to work on windows. - Panel = element(Id, Windows), + %% Sigh workaround bug on MacOSX (Id in paint event is always 0) + %% Panel = element(Id, Windows), + Id = if Panel =:= element(?RQ_W, Windows) -> ?RQ_W; + Panel =:= element(?MEM_W, Windows) -> ?MEM_W; + Panel =:= element(?IO_W, Windows) -> ?IO_W + end, DC = wxPaintDC:new(Panel), %% Nothing is drawn until wxPaintDC is destroyed. - try draw(Offset, Id, DC, Panel, Paint, Data, Active) + try + draw(Offset, Id, DC, Panel, Paint, Data, Active) catch _:Err -> io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()]) end, @@ -253,6 +259,7 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) Start = max(61-Len, 0)*WS+X0 - Offset*WS, case Hs of [] -> ignore; + [_] -> ignore; _ -> Draw = fun(N) -> Lines = make_lines(Hs, Start, N, {X0,round(Max*HS)}, Y0, WS, HS), -- cgit v1.2.3 From d6949858764602ead48c553327153a215393f77a Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 17 Feb 2012 08:32:35 +0100 Subject: [observer] Use wxGC for drawing --- lib/observer/src/observer_app_wx.erl | 85 ++++++++++++----------- lib/observer/src/observer_perf_wx.erl | 125 ++++++++++++++++++---------------- 2 files changed, 111 insertions(+), 99 deletions(-) diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 7b4f7c4ce5..cb5f8c1259 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -63,6 +63,8 @@ -define(ID_TRACE_TREE_PIDS, 106). -define(ID_TRACE_TREE_NAMES, 107). +-define(wxGC, wxGraphicsContext). + start_link(Notebook, Parent) -> wx_object:start_link(?MODULE, [Notebook, Parent], []). @@ -100,22 +102,22 @@ init([Notebook, Parent]) -> wxPanel:connect(DrawingArea, left_dclick), wxPanel:connect(DrawingArea, right_down), - DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + %% DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + DefFont = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL), SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), + GreyBrush = wxBrush:new({230,230,240}), SelBrush = wxBrush:new(SelCol), LinkPen = wxPen:new(SelCol, [{width, 2}]), - %% GC = wxGraphicsContext:create(DrawingArea), - %% _Font = wxGraphicsContext:createFont(GC, DefFont), process_flag(trap_exit, true), {Panel, #state{parent=Parent, panel =Panel, apps_w=Apps, app_w =DrawingArea, - paint=#paint{font= DefFont, - pen= ?wxBLACK_PEN, - brush=?wxLIGHT_GREY_BRUSH, - sel= SelBrush, - links=LinkPen + paint=#paint{font = DefFont, + pen = wxPen:new({80,80,80}, [{width, 2}]), + brush= GreyBrush, + sel = SelBrush, + links= LinkPen } }}. @@ -219,9 +221,13 @@ handle_sync_event(#wx{event = #wxPaint{}},_, #state{app_w=DA, app=App, sel=Sel, paint=Paint}) -> %% PaintDC must be created in a callback to work on windows. DC = wxPaintDC:new(DA), - wxScrolledWindow:doPrepareDC(DA,DC), + GC = ?wxGC:create(DC), + %% Argh must handle scrolling when using ?wxGC + {Sx,Sy} = wxScrolledWindow:calcScrolledPosition(DA, {0,0}), + ?wxGC:translate(GC, Sx,Sy), %% Nothing is drawn until wxPaintDC is destroyed. - draw(DC, App, Sel, Paint), + draw(GC, App, Sel, Paint), + ?wxGC:destroy(GC), wxPaintDC:destroy(DC), ok. %%%%%%%%%% @@ -250,12 +256,18 @@ handle_info(not_active, State = #state{appmon=AppMon, current=Prev}) -> {noreply, State}; handle_info({delivery, Pid, app_ctrl, _, Apps0}, - State = #state{appmon=Pid, apps_w=LBox}) -> + State = #state{appmon=Pid, apps_w=LBox, current=Curr0}) -> Apps = [atom_to_list(App) || {_, App, {_, _, _}} <- Apps0], wxListBox:clear(LBox), wxListBox:appendStrings(LBox, [App || App <- lists:sort(Apps)]), - {noreply, State}; - + case Apps of + [App|_] when Curr0 =:= undefined -> + Curr = list_to_atom(App), + appmon_info:app(Pid, Curr, true, []), + {noreply, State#state{current=Curr}}; + _ -> + {noreply, State} + end; handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}}, State = #state{panel=Panel}) -> wxWindow:refresh(Panel), @@ -264,7 +276,10 @@ handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}}, handle_info({delivery, Pid, app, Curr, AppData}, State = #state{panel=Panel, appmon=Pid, current=Curr, app_w=AppWin, paint=#paint{font=Font}}) -> - App = build_tree(AppData, {AppWin,Font}), + GC = ?wxGC:create(AppWin), + ?wxGC:setFont(GC, Font, {0,0,0}), + App = build_tree(AppData, {GC,Font}), + ?wxGC:destroy(GC), setup_scrollbar(AppWin, App), wxWindow:refresh(Panel), wxWindow:layout(Panel), @@ -432,15 +447,15 @@ middle([{#box{y=Y0},_}|List], _) -> {#box{y=Y1},_} = lists:last(List), (Y0+Y1) div 2. -box(Str0, N2P, {Win,Font}) -> +box(Str0, N2P, {Win,_Font}) -> Pid = gb_trees:get(Str0, N2P), Str = if hd(Str0) =:= $< -> lists:append(io_lib:format("~w", [Pid])); true -> Str0 end, - {TW,TH, _, _} = wxWindow:getTextExtent(Win, Str, [{theFont, Font}]), + {TW,TH, _, _} = ?wxGC:getTextExtent(Win, Str), Data = #str{text=Str, x=?BX_HE, y=?BY_HE, pid=Pid}, %% Add pid - #box{w=TW+?BX_E, h=TH+?BY_E, s1=Data}. + #box{w=round(TW)+?BX_E, h=round(TH)+?BY_E, s1=Data}. box_to_pid(#box{s1=#str{pid=Pid}}) -> Pid. box_to_reg(#box{s1=#str{text=[$<|_], pid=Pid}}) -> Pid; @@ -458,39 +473,30 @@ draw(_DC, undefined, _, _) -> ok; draw(DC, #app{dim={_W,_H}, ptree=Tree, links=Links}, Sel, #paint{font=Font, pen=Pen, brush=Brush, links=LPen, sel=SelBrush}) -> - %% Canvas = wxGraphicsContext:create(DC), - %% Pen = wxGraphicsContext:createPen(Canvas, ?wxBLACK_PEN), - %% wxGraphicsContext:setPen(Canvas, Pen), - %% Brush = wxGraphicsContext:createBrush(Canvas, ?wxLIGHT_GREY_BRUSH), - %% wxGraphicsContext:setBrush(Canvas, Brush), - %% Font = wxGraphicsContext:createFont(Canvas, wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT)), - %% wxGraphicsContext:setFont(Canvas, Font), - %% draw_tree(Tree, Canvas). - wxDC:setPen(DC, LPen), + ?wxGC:setPen(DC, LPen), [draw_xlink(Link, DC) || Link <- Links], - wxDC:setPen(DC, Pen), - %% wxDC:drawRectangle(DC, {2,2}, {W-2,H-2}), %% DEBUG - wxDC:setBrush(DC, Brush), - wxDC:setFont(DC, Font), + ?wxGC:setPen(DC, Pen), + %% ?wxGC:drawRectangle(DC, 2,2, _W-2,_H-2), %% DEBUG + ?wxGC:setBrush(DC, Brush), + ?wxGC:setFont(DC, Font, {0,0,0}), draw_tree(Tree, root, DC), case Sel of undefined -> ok; {#box{x=X,y=Y,w=W,h=H,s1=Str1}, _} -> - wxDC:setBrush(DC, SelBrush), - wxDC:drawRoundedRectangle(DC, {X-1,Y-1}, {W+2,H+2}, 8.0), + ?wxGC:setBrush(DC, SelBrush), + ?wxGC:drawRoundedRectangle(DC, X-1,Y-1, W+2,H+2, 8.0), draw_str(DC, Str1, X, Y) end. draw_tree({Box=#box{x=X,y=Y,w=W,h=H,s1=Str1}, Chs}, Parent, DC) -> - %%wxGraphicsContext:drawRoundedRectangle(DC, float(X), float(Y), float(W), float(H), 8.0), - wxDC:drawRoundedRectangle(DC, {X,Y}, {W,H}, 8.0), + ?wxGC:drawRoundedRectangle(DC, X,Y, W,H, 8.0), draw_str(DC, Str1, X, Y), Dot = case Chs of [] -> ok; [{#box{x=CX0},_}|_] -> CY = Y+(H div 2), CX = CX0-(?BB_X div 2), - wxDC:drawLine(DC, {X+W, CY}, {CX, CY}), + ?wxGC:strokeLine(DC, X+W, CY, CX, CY), {CX, CY} end, draw_link(Parent, Box, DC), @@ -500,9 +506,9 @@ draw_link({CX,CY}, #box{x=X,y=Y0,h=H}, DC) -> Y = Y0+(H div 2), case Y =:= CY of true -> - wxDC:drawLine(DC, {CX, CY}, {X, CY}); + ?wxGC:strokeLine(DC, CX, CY, X, CY); false -> - wxDC:drawLines(DC, [{CX, CY}, {CX, Y}, {X,Y}]) + ?wxGC:strokeLines(DC, [{CX, CY}, {CX, Y}, {X,Y}]) end; draw_link(_, _, _) -> ok. @@ -521,9 +527,8 @@ draw_xlink(X0, Y00, X1, Y11, BH, DC) -> {Y0,Y1} = if Y00 < Y11 -> {Y00+BH-6, Y11+6}; true -> {Y00+6, Y11+BH-6} end, - wxDC:drawLines(DC, [{X0,Y0}, {X0+5,Y0}, {X1-5,Y1}, {X1,Y1}]). + ?wxGC:strokeLines(DC, [{X0,Y0}, {X0+5,Y0}, {X1-5,Y1}, {X1,Y1}]). draw_str(DC, #str{x=Sx,y=Sy, text=Text}, X, Y) -> - %%wxGraphicsContext:drawText(DC, Text, float(Sx+X), float(Sy+Y)); - wxDC:drawText(DC, Text, {X+Sx,Y+Sy}); + ?wxGC:drawText(DC, Text, X+Sx,Y+Sy); draw_str(_, _, _, _) -> ok. diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 8f4e317d99..96e433cbf2 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -39,6 +39,8 @@ appmon }). +-define(wxGC, wxGraphicsContext). + -record(paint, {font, small, pen, pen2, pens}). -define(RQ_W, 1). @@ -74,11 +76,12 @@ init([Notebook, Parent]) -> wxPanel:connect(IO, paint, [callback]), wxPanel:connect(MEM, paint, [callback]), - DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), - DefSize = wxFont:getPointSize(DefFont), - DefFamily = wxFont:getFamily(DefFont), - Font = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), - SmallFont = wxFont:new(10, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + % DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + %% DefSize = wxFont:getPointSize(DefFont), + %% DefFamily = wxFont:getFamily(DefFont), + %% Font = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), + Font = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), + SmallFont = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), BlackPen = wxPen:new({0,0,0}, [{width, 2}]), Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], process_flag(trap_exit, true), @@ -118,12 +121,14 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, Panel =:= element(?IO_W, Windows) -> ?IO_W end, DC = wxPaintDC:new(Panel), + GC = ?wxGC:create(DC), %% Nothing is drawn until wxPaintDC is destroyed. try - draw(Offset, Id, DC, Panel, Paint, Data, Active) + draw(Offset, Id, GC, Panel, Paint, Data, Active) catch _:Err -> io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()]) end, + ?wxGC:destroy(GC), wxPaintDC:destroy(DC), ok. %%%%%%%%%% @@ -250,21 +255,22 @@ calc_delta([], []) -> []. draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) -> %% This can be optimized a lot by collecting data once %% and draw to memory and then blit memory and only draw new entries in new memory - %% area. + %% area. Hmm now rewritten to use ?wxGC I don't now if it is feasable. {Len, Max0, Hs} = collect_data(Id, Data), Max = calc_max(Max0), NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, Size = wxWindow:getClientSize(Panel), {X0,Y0,WS,HS} = draw_borders(Id, NoGraphs, DC, Size, Max, Paint), + Last = 60*WS+X0-1, Start = max(61-Len, 0)*WS+X0 - Offset*WS, case Hs of [] -> ignore; [_] -> ignore; _ -> Draw = fun(N) -> - Lines = make_lines(Hs, Start, N, {X0,round(Max*HS)}, Y0, WS, HS), - wxDC:setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), - wxDC:drawLines(DC, Lines), + Lines = make_lines(Hs, Start, N, {X0,Max*HS,Last}, Y0, WS, HS), + ?wxGC:setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), + ?wxGC:strokeLines(DC, Lines), N+1 end, [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)] @@ -272,9 +278,8 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) case Active of false -> NotActive = "Service not available", - wxDC:setTextForeground(DC, {0,0,0}), - wxDC:setFont(DC, Small), - wxDC:drawText(DC, NotActive, {X0 + 100, element(2,Size) div 2}); + ?wxGC:setFont(DC, Small, {0,0,0}), + ?wxGC:drawText(DC, NotActive, X0 + 100, element(2,Size) div 2); true -> ignore end, @@ -284,30 +289,30 @@ make_lines(Ds = [Data|_], PX, N, Clip, ZeroY, WS, HS) -> Y = element(N,Data), make_lines(Ds, PX, N, Clip, ZeroY, WS, HS, Y, []). -make_lines([D1 | Ds = [D2|Rest]], PX, N, Clip={Cx,Cy}, ZeroY, WS, HS, Y0, Acc0) -> +make_lines([D1 | Ds = [D2|Rest]], PX, N, Clip={Cx,Cy, _}, ZeroY, WS, HS, Y0, Acc0) -> Y1 = element(N,D1), Y2 = element(N,D2), Y3 = case Rest of [D3|_] -> element(N,D3); [] -> Y2 end, - This = {max(Cx, round(PX)),ZeroY-min(Cy,round(Y1*HS))}, - Acc = make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,[This|Acc0]), + This = {max(Cx, PX),ZeroY-min(Cy,Y1*HS)}, + Acc = if (abs(Y1-Y2) * HS) < 3.0 -> [This|Acc0]; + WS < 3.0 -> [This|Acc0]; + PX < Cx -> + make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,Acc0); + true -> + make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,[This|Acc0]) + end, make_lines(Ds, PX+WS, N, Clip, ZeroY, WS, HS, Y1, Acc); -make_lines([D1], PX, N, _Clip, ZeroY, _WS, HS, _Y0, Acc) -> +make_lines([D1], _PX, N, {_,Cy,Last}, ZeroY, _WS, HS, _Y0, Acc) -> Y1 = element(N,D1), - [{round(PX),ZeroY-round(Y1*HS)}|Acc]. - -make_splines(_Y0,Y1,Y2,_Y3, _PX, _Clip,_ZeroY, _WS,HS, Acc) - when (abs(Y1-Y2) * HS) < 3.0 -> - Acc; -make_splines(_Y0,_Y1,_Y2,_Y3, _PX, _Clip,_ZeroY, WS,_HS, Acc) - when WS < 3.0 -> - Acc; + [{Last,ZeroY-min(Cy, Y1*HS)}|Acc]. + make_splines(Y00,Y10,Y20,Y30,PX,Clip,ZeroY,WS,HS,Acc) -> Y1 = Y10*HS, Y2 = Y20*HS, - Steps = round(min(abs(Y1-Y2), WS)), + Steps = min(abs(Y1-Y2), WS), if Steps > 2 -> Y0 = Y00*HS, Y3 = Y30*HS, @@ -318,15 +323,15 @@ make_splines(Y00,Y10,Y20,Y30,PX,Clip,ZeroY,WS,HS,Acc) -> Acc end. -splines(N, XD, XD0, Tan, Y1,Y2, PX0, Clip={Cx,Cy},ZeroY, WS, Acc) when N > 0 -> +splines(N, XD, XD0, Tan, Y1,Y2, PX0, Clip={Cx,Cy,_},ZeroY, WS, Acc) when N > 0 -> PX = PX0+WS, Delta = XD+XD0, if PX < Cx -> splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, Acc); true -> - Y = min(Cy, max(0,round(spline(Delta, Tan, Y1,Y2)))), + Y = min(Cy, max(0,spline(Delta, Tan, Y1,Y2))), splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, - [{round(PX), ZeroY-Y}|Acc]) + [{PX, ZeroY-Y}|Acc]) end; splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _Clip,_ZeroY, _WS, Acc) -> Acc. @@ -357,8 +362,10 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, Str1 = observer_lib:to_str(MaxUnit), Str2 = observer_lib:to_str(MaxUnit div 2), Str3 = observer_lib:to_str(0), - {TW,TH} = wxDC:getTextExtent(DC, Str1), - {SpaceW, _} = wxDC:getTextExtent(DC, " "), + + ?wxGC:setFont(DC, Font, {0,0,0}), + {TW,TH,_,_} = ?wxGC:getTextExtent(DC, Str1), + {SpaceW, _,_,_} = ?wxGC:getTextExtent(DC, "W"), GraphX0 = ?BW+TW+?BW, GraphX1 = W-?BW*4, @@ -366,60 +373,60 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, MaxTextY = ?BH+TH+?BH, BottomTextY = H-?BH-TH, SecondsY = BottomTextY - ?BH - TH, - GraphY0 = MaxTextY + (TH div 2), + GraphY0 = MaxTextY + (TH / 2), GraphY1 = SecondsY - ?BH, GraphW = GraphX1-GraphX0-1, GraphH = GraphY1-GraphY0-1, - GraphY25 = GraphY0 + (GraphY1 - GraphY0) div 4, - GraphY50 = GraphY0 + (GraphY1 - GraphY0) div 2, - GraphY75 = GraphY0 + 3*(GraphY1 - GraphY0) div 4, + GraphY25 = GraphY0 + (GraphY1 - GraphY0) / 4, + GraphY50 = GraphY0 + (GraphY1 - GraphY0) / 2, + GraphY75 = GraphY0 + 3*(GraphY1 - GraphY0) / 4, ScaleW = GraphW / 60, ScaleH = GraphH / Max, - wxDC:setFont(DC, Small), + ?wxGC:setFont(DC, Small, {0,0,0}), Align = fun(Str, Y) -> - {StrW, _} = wxDC:getTextExtent(DC, Str), - wxDC:drawText(DC, Str, {GraphX0 - StrW - ?BW, Y}) + {StrW, _, _, _} = ?wxGC:getTextExtent(DC, Str), + ?wxGC:drawText(DC, Str, GraphX0 - StrW - ?BW, Y) end, Align(Str1, MaxTextY), - Align(Str2, GraphY50 - (TH div 2)), - Align(Str3, GraphY1 - (TH div 2) + 1), + Align(Str2, GraphY50 - (TH / 2)), + Align(Str3, GraphY1 - (TH / 2) + 1), - wxDC:setPen(DC, Pen), + ?wxGC:setPen(DC, Pen), DrawSecs = fun(Secs, Pos) -> Str = [observer_lib:to_str(Secs)|" s"], - X = round(GraphX0+Pos), - wxDC:drawText(DC, Str, {X-SpaceW, SecondsY}), - wxDC:drawLine(DC, {X, GraphY0}, {X, GraphY1+5}), + X = GraphX0+Pos, + ?wxGC:drawText(DC, Str, X-SpaceW, SecondsY), + ?wxGC:strokeLine(DC, X, GraphY0, X, GraphY1+5), Pos + 10*ScaleW end, lists:foldl(DrawSecs, 0, lists:seq(60,0, -10)), - wxDC:drawLine(DC, {GraphX0-3, GraphY25}, {GraphX1, GraphY25}), - wxDC:drawLine(DC, {GraphX0-3, GraphY50}, {GraphX1, GraphY50}), - wxDC:drawLine(DC, {GraphX0-3, GraphY75}, {GraphX1, GraphY75}), + ?wxGC:strokeLine(DC, GraphX0-3, GraphY25, GraphX1, GraphY25), + ?wxGC:strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50), + ?wxGC:strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75), - wxDC:setPen(DC, Pen2), - wxDC:drawLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, - {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, - {GraphX0, GraphY0-1}]), + ?wxGC:setPen(DC, Pen2), + ?wxGC:strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, + {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, + {GraphX0, GraphY0-1}]), - wxDC:setFont(DC, Font), + ?wxGC:setFont(DC, Font, {0,0,0}), case Type of - ?RQ_W -> wxDC:drawText(DC, "Scheduler Utilization (%) ", {TopTextX,?BH}); - ?MEM_W -> wxDC:drawText(DC, "Memory Usage " ++ Unit, {TopTextX,?BH}); - ?IO_W -> wxDC:drawText(DC, "IO Usage " ++ Unit, {TopTextX,?BH}) + ?RQ_W -> ?wxGC:drawText(DC, "Scheduler Utilization (%) ", TopTextX,?BH); + ?MEM_W -> ?wxGC:drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH); + ?IO_W -> ?wxGC:drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH) end, Text = fun(X,Y, Str, PenId) -> if PenId == 0 -> - wxDC:setTextForeground(DC, {0,0,0}); + ?wxGC:setFont(DC, Font, {0,0,0}); PenId > 0 -> Id = 1 + ((PenId-1) rem tuple_size(colors())), - wxDC:setTextForeground(DC, element(Id, colors())) + ?wxGC:setFont(DC, Font, element(Id, colors())) end, - wxDC:drawText(DC, Str, {X, Y}), - {StrW, _} = wxDC:getTextExtent(DC, Str), + ?wxGC:drawText(DC, Str, X, Y), + {StrW, _, _, _} = ?wxGC:getTextExtent(DC, Str), StrW + X + SpaceW end, case Type of -- cgit v1.2.3 From 5584462be1c6dce1990a9031c0e43a89e758a263 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 20 Feb 2012 12:51:37 +0100 Subject: [observer] Add fallback drawing to raster api (wxDC) Sigh, seems that Suse-11 does not default deliver wxWidgets with wxGRAPHICS_CONTEXT enabled, add fallback to use wxDC again. --- lib/observer/src/observer_app_wx.erl | 95 +++++++++++++--------- lib/observer/src/observer_perf_wx.erl | 147 +++++++++++++++++++++++++--------- 2 files changed, 168 insertions(+), 74 deletions(-) diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index cb5f8c1259..900fba911a 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -27,6 +27,12 @@ -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). +%% Import drawing wrappers +-import(observer_perf_wx, [haveGC/1, + setPen/2, setFont/3, setBrush/2, + strokeLine/5, strokeLines/2, drawRoundedRectangle/6, + drawText/4, getTextExtent/2]). + -record(state, { parent, @@ -37,7 +43,8 @@ current, app, sel, - appmon + appmon, + usegc = false }). -record(paint, {font, pen, brush, sel, links}). @@ -102,8 +109,11 @@ init([Notebook, Parent]) -> wxPanel:connect(DrawingArea, left_dclick), wxPanel:connect(DrawingArea, right_down), - %% DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), - DefFont = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL), + UseGC = haveGC(DrawingArea), + Font = case UseGC of + true -> wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT); + false -> wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL) + end, SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), GreyBrush = wxBrush:new({230,230,240}), SelBrush = wxBrush:new(SelCol), @@ -113,7 +123,8 @@ init([Notebook, Parent]) -> panel =Panel, apps_w=Apps, app_w =DrawingArea, - paint=#paint{font = DefFont, + usegc = UseGC, + paint=#paint{font = Font, pen = wxPen:new({80,80,80}, [{width, 2}]), brush= GreyBrush, sel = SelBrush, @@ -218,16 +229,23 @@ handle_event(Event, _State) -> %%%%%%%%%% handle_sync_event(#wx{event = #wxPaint{}},_, - #state{app_w=DA, app=App, sel=Sel, paint=Paint}) -> + #state{app_w=DA, app=App, sel=Sel, paint=Paint, usegc=UseGC}) -> %% PaintDC must be created in a callback to work on windows. DC = wxPaintDC:new(DA), - GC = ?wxGC:create(DC), - %% Argh must handle scrolling when using ?wxGC - {Sx,Sy} = wxScrolledWindow:calcScrolledPosition(DA, {0,0}), - ?wxGC:translate(GC, Sx,Sy), + GC = case UseGC of + true -> + GC0 = ?wxGC:create(DC), + %% Argh must handle scrolling when using ?wxGC + {Sx,Sy} = wxScrolledWindow:calcScrolledPosition(DA, {0,0}), + ?wxGC:translate(GC0, Sx,Sy), + GC0; + false -> + wxScrolledWindow:doPrepareDC(DA,DC), + DC + end, %% Nothing is drawn until wxPaintDC is destroyed. - draw(GC, App, Sel, Paint), - ?wxGC:destroy(GC), + draw({UseGC, GC}, App, Sel, Paint), + UseGC andalso ?wxGC:destroy(GC), wxPaintDC:destroy(DC), ok. %%%%%%%%%% @@ -274,12 +292,17 @@ handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}}, {noreply, State#state{app=undefined, sel=undefined}}; handle_info({delivery, Pid, app, Curr, AppData}, - State = #state{panel=Panel, appmon=Pid, current=Curr, + State = #state{panel=Panel, appmon=Pid, current=Curr, usegc=UseGC, app_w=AppWin, paint=#paint{font=Font}}) -> - GC = ?wxGC:create(AppWin), - ?wxGC:setFont(GC, Font, {0,0,0}), - App = build_tree(AppData, {GC,Font}), - ?wxGC:destroy(GC), + GC = if UseGC -> ?wxGC:create(AppWin); + true -> wxWindowDC:new(AppWin) + end, + FontW = {UseGC, GC}, + setFont(FontW, Font, {0,0,0}), + App = build_tree(AppData, FontW), + if UseGC -> ?wxGC:destroy(GC); + true -> wxWindowDC:destroy(GC) + end, setup_scrollbar(AppWin, App), wxWindow:refresh(Panel), wxWindow:layout(Panel), @@ -374,11 +397,11 @@ locate_box(From, []) -> {false, From}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -build_tree({Root, P2Name, Links, XLinks0}, Font) -> +build_tree({Root, P2Name, Links, XLinks0}, FontW) -> Fam = sofs:relation_to_family(sofs:relation(Links)), Name2P = gb_trees:from_orddict(lists:sort([{Name,Pid} || {Pid,Name} <- P2Name])), Lookup = gb_trees:from_orddict(sofs:to_external(Fam)), - {_, Tree0} = build_tree2(Root, Lookup, Name2P, Font), + {_, Tree0} = build_tree2(Root, Lookup, Name2P, FontW), {Tree, Dim} = calc_tree_size(Tree0), Fetch = fun({From, To}, Acc) -> try {value, ToPid} = gb_trees:lookup(To, Name2P), @@ -391,18 +414,18 @@ build_tree({Root, P2Name, Links, XLinks0}, Font) -> XLinks = lists:foldl(Fetch, [], XLinks0), #app{ptree=Tree, dim=Dim, links=XLinks}. -build_tree2(Root, Tree0, N2P, Font) -> +build_tree2(Root, Tree0, N2P, FontW) -> case gb_trees:lookup(Root, Tree0) of - none -> {Tree0, {box(Root, N2P, Font), []}}; + none -> {Tree0, {box(Root, N2P, FontW), []}}; {value, Children} -> Tree1 = gb_trees:delete(Root, Tree0), {Tree, CHs} = lists:foldr(fun("port " ++_, Acc) -> Acc; %% Skip ports (Child,{T0, Acc}) -> - {T, C} = build_tree2(Child, T0, N2P, Font), + {T, C} = build_tree2(Child, T0, N2P, FontW), {T, [C|Acc]} end, {Tree1, []}, Children), - {Tree, {box(Root, N2P, Font), CHs}} + {Tree, {box(Root, N2P, FontW), CHs}} end. calc_tree_size(Tree) -> @@ -447,12 +470,12 @@ middle([{#box{y=Y0},_}|List], _) -> {#box{y=Y1},_} = lists:last(List), (Y0+Y1) div 2. -box(Str0, N2P, {Win,_Font}) -> +box(Str0, N2P, FontW) -> Pid = gb_trees:get(Str0, N2P), Str = if hd(Str0) =:= $< -> lists:append(io_lib:format("~w", [Pid])); true -> Str0 end, - {TW,TH, _, _} = ?wxGC:getTextExtent(Win, Str), + {TW,TH} = getTextExtent(FontW, Str), Data = #str{text=Str, x=?BX_HE, y=?BY_HE, pid=Pid}, %% Add pid #box{w=round(TW)+?BX_E, h=round(TH)+?BY_E, s1=Data}. @@ -473,30 +496,30 @@ draw(_DC, undefined, _, _) -> ok; draw(DC, #app{dim={_W,_H}, ptree=Tree, links=Links}, Sel, #paint{font=Font, pen=Pen, brush=Brush, links=LPen, sel=SelBrush}) -> - ?wxGC:setPen(DC, LPen), + setPen(DC, LPen), [draw_xlink(Link, DC) || Link <- Links], - ?wxGC:setPen(DC, Pen), + setPen(DC, Pen), %% ?wxGC:drawRectangle(DC, 2,2, _W-2,_H-2), %% DEBUG - ?wxGC:setBrush(DC, Brush), - ?wxGC:setFont(DC, Font, {0,0,0}), + setBrush(DC, Brush), + setFont(DC, Font, {0,0,0}), draw_tree(Tree, root, DC), case Sel of undefined -> ok; {#box{x=X,y=Y,w=W,h=H,s1=Str1}, _} -> - ?wxGC:setBrush(DC, SelBrush), - ?wxGC:drawRoundedRectangle(DC, X-1,Y-1, W+2,H+2, 8.0), + setBrush(DC, SelBrush), + drawRoundedRectangle(DC, X-1,Y-1, W+2,H+2, 8.0), draw_str(DC, Str1, X, Y) end. draw_tree({Box=#box{x=X,y=Y,w=W,h=H,s1=Str1}, Chs}, Parent, DC) -> - ?wxGC:drawRoundedRectangle(DC, X,Y, W,H, 8.0), + drawRoundedRectangle(DC, X,Y, W,H, 8.0), draw_str(DC, Str1, X, Y), Dot = case Chs of [] -> ok; [{#box{x=CX0},_}|_] -> CY = Y+(H div 2), CX = CX0-(?BB_X div 2), - ?wxGC:strokeLine(DC, X+W, CY, CX, CY), + strokeLine(DC, X+W, CY, CX, CY), {CX, CY} end, draw_link(Parent, Box, DC), @@ -506,9 +529,9 @@ draw_link({CX,CY}, #box{x=X,y=Y0,h=H}, DC) -> Y = Y0+(H div 2), case Y =:= CY of true -> - ?wxGC:strokeLine(DC, CX, CY, X, CY); + strokeLine(DC, CX, CY, X, CY); false -> - ?wxGC:strokeLines(DC, [{CX, CY}, {CX, Y}, {X,Y}]) + strokeLines(DC, [{CX, CY}, {CX, Y}, {X,Y}]) end; draw_link(_, _, _) -> ok. @@ -527,8 +550,8 @@ draw_xlink(X0, Y00, X1, Y11, BH, DC) -> {Y0,Y1} = if Y00 < Y11 -> {Y00+BH-6, Y11+6}; true -> {Y00+6, Y11+BH-6} end, - ?wxGC:strokeLines(DC, [{X0,Y0}, {X0+5,Y0}, {X1-5,Y1}, {X1,Y1}]). + strokeLines(DC, [{X0,Y0}, {X0+5,Y0}, {X1-5,Y1}, {X1,Y1}]). draw_str(DC, #str{x=Sx,y=Sy, text=Text}, X, Y) -> - ?wxGC:drawText(DC, Text, X+Sx,Y+Sy); + drawText(DC, Text, X+Sx,Y+Sy); draw_str(_, _, _, _) -> ok. diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 96e433cbf2..c4659d1ea1 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -23,6 +23,12 @@ -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_sync_event/3, handle_cast/2]). +%% Drawing wrappers for DC and GC areas +-export([haveGC/1, + setPen/2, setFont/3, setBrush/2, + strokeLine/5, strokeLines/2, drawRoundedRectangle/6, + drawText/4, getTextExtent/2]). + -behaviour(wx_object). -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). @@ -36,7 +42,8 @@ data = {0, queue:new()}, panel, paint, - appmon + appmon, + usegc = false }). -define(wxGC, wxGraphicsContext). @@ -76,11 +83,18 @@ init([Notebook, Parent]) -> wxPanel:connect(IO, paint, [callback]), wxPanel:connect(MEM, paint, [callback]), - % DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), - %% DefSize = wxFont:getPointSize(DefFont), - %% DefFamily = wxFont:getFamily(DefFont), - %% Font = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), - Font = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), + UseGC = haveGC(Panel), + Font = case UseGC of + true -> + %% Def font is really small when using Graphics contexts for some reason + %% Hardcode it + wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD); + false -> + DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + DefSize = wxFont:getPointSize(DefFont), + DefFamily = wxFont:getFamily(DefFont), + wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD) + end, SmallFont = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), BlackPen = wxPen:new({0,0,0}, [{width, 2}]), Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], @@ -88,6 +102,7 @@ init([Notebook, Parent]) -> {Panel, #state{parent=Parent, panel =Panel, windows = {CPU, MEM, IO}, + usegc=UseGC, paint=#paint{font = Font, small = SmallFont, pen = ?wxGREY_PEN, @@ -112,7 +127,7 @@ handle_event(Event, _State) -> %%%%%%%%%% handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, #state{active=Active, offset=Offset, paint=Paint, - windows=Windows, data=Data}) -> + windows=Windows, data=Data, usegc=UseGC}) -> %% PaintDC must be created in a callback to work on windows. %% Sigh workaround bug on MacOSX (Id in paint event is always 0) %% Panel = element(Id, Windows), @@ -121,14 +136,17 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, Panel =:= element(?IO_W, Windows) -> ?IO_W end, DC = wxPaintDC:new(Panel), - GC = ?wxGC:create(DC), + GC = case UseGC of + true -> ?wxGC:create(DC); + false -> DC + end, %% Nothing is drawn until wxPaintDC is destroyed. try - draw(Offset, Id, GC, Panel, Paint, Data, Active) + draw(Offset, Id, {UseGC, GC}, Panel, Paint, Data, Active) catch _:Err -> io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()]) end, - ?wxGC:destroy(GC), + UseGC andalso ?wxGC:destroy(GC), wxPaintDC:destroy(DC), ok. %%%%%%%%%% @@ -269,8 +287,8 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) _ -> Draw = fun(N) -> Lines = make_lines(Hs, Start, N, {X0,Max*HS,Last}, Y0, WS, HS), - ?wxGC:setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), - ?wxGC:strokeLines(DC, Lines), + setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), + strokeLines(DC, Lines), N+1 end, [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)] @@ -278,8 +296,8 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) case Active of false -> NotActive = "Service not available", - ?wxGC:setFont(DC, Small, {0,0,0}), - ?wxGC:drawText(DC, NotActive, X0 + 100, element(2,Size) div 2); + setFont(DC, Small, {0,0,0}), + drawText(DC, NotActive, X0 + 100, element(2,Size) div 2); true -> ignore end, @@ -363,9 +381,9 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, Str2 = observer_lib:to_str(MaxUnit div 2), Str3 = observer_lib:to_str(0), - ?wxGC:setFont(DC, Font, {0,0,0}), - {TW,TH,_,_} = ?wxGC:getTextExtent(DC, Str1), - {SpaceW, _,_,_} = ?wxGC:getTextExtent(DC, "W"), + setFont(DC, Font, {0,0,0}), + {TW,TH} = getTextExtent(DC, Str1), + {SpaceW, _} = getTextExtent(DC, "W"), GraphX0 = ?BW+TW+?BW, GraphX1 = W-?BW*4, @@ -383,50 +401,50 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, ScaleW = GraphW / 60, ScaleH = GraphH / Max, - ?wxGC:setFont(DC, Small, {0,0,0}), + setFont(DC, Small, {0,0,0}), Align = fun(Str, Y) -> - {StrW, _, _, _} = ?wxGC:getTextExtent(DC, Str), - ?wxGC:drawText(DC, Str, GraphX0 - StrW - ?BW, Y) + {StrW, _} = getTextExtent(DC, Str), + drawText(DC, Str, GraphX0 - StrW - ?BW, Y) end, Align(Str1, MaxTextY), Align(Str2, GraphY50 - (TH / 2)), Align(Str3, GraphY1 - (TH / 2) + 1), - ?wxGC:setPen(DC, Pen), + setPen(DC, Pen), DrawSecs = fun(Secs, Pos) -> Str = [observer_lib:to_str(Secs)|" s"], X = GraphX0+Pos, - ?wxGC:drawText(DC, Str, X-SpaceW, SecondsY), - ?wxGC:strokeLine(DC, X, GraphY0, X, GraphY1+5), + drawText(DC, Str, X-SpaceW, SecondsY), + strokeLine(DC, X, GraphY0, X, GraphY1+5), Pos + 10*ScaleW end, lists:foldl(DrawSecs, 0, lists:seq(60,0, -10)), - ?wxGC:strokeLine(DC, GraphX0-3, GraphY25, GraphX1, GraphY25), - ?wxGC:strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50), - ?wxGC:strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75), + strokeLine(DC, GraphX0-3, GraphY25, GraphX1, GraphY25), + strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50), + strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75), - ?wxGC:setPen(DC, Pen2), - ?wxGC:strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, - {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, - {GraphX0, GraphY0-1}]), + setPen(DC, Pen2), + strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, + {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, + {GraphX0, GraphY0-1}]), - ?wxGC:setFont(DC, Font, {0,0,0}), + setFont(DC, Font, {0,0,0}), case Type of - ?RQ_W -> ?wxGC:drawText(DC, "Scheduler Utilization (%) ", TopTextX,?BH); - ?MEM_W -> ?wxGC:drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH); - ?IO_W -> ?wxGC:drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH) + ?RQ_W -> drawText(DC, "Scheduler Utilization (%) ", TopTextX,?BH); + ?MEM_W -> drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH); + ?IO_W -> drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH) end, Text = fun(X,Y, Str, PenId) -> if PenId == 0 -> - ?wxGC:setFont(DC, Font, {0,0,0}); + setFont(DC, Font, {0,0,0}); PenId > 0 -> Id = 1 + ((PenId-1) rem tuple_size(colors())), - ?wxGC:setFont(DC, Font, element(Id, colors())) + setFont(DC, Font, element(Id, colors())) end, - ?wxGC:drawText(DC, Str, X, Y), - {StrW, _, _, _} = ?wxGC:getTextExtent(DC, Str), + drawText(DC, Str, X, Y), + {StrW, _} = getTextExtent(DC, Str), StrW + X + SpaceW end, case Type of @@ -483,3 +501,56 @@ colors() -> {240, 200, 80}, {140, 2, 140}, {100, 200, 240}, {100, 240, 100} }. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% wxDC and ?wxGC wrappers + +haveGC(Win) -> + try + GC = ?wxGC:create(Win), + ?wxGC:destroy(GC), + true + catch _:_ -> false + end. + +setPen({false, DC}, Pen) -> + wxDC:setPen(DC, Pen); +setPen({true, GC}, Pen) -> + ?wxGC:setPen(GC, Pen). + +setFont({false, DC}, Font, Color) -> + wxDC:setTextForeground(DC, Color), + wxDC:setFont(DC, Font); +setFont({true, GC}, Font, Color) -> + ?wxGC:setFont(GC, Font, Color). + +setBrush({false, DC}, Brush) -> + wxDC:setBrush(DC, Brush); +setBrush({true, GC}, Brush) -> + ?wxGC:setBrush(GC, Brush). + +strokeLine({false, DC}, X0, Y0, X1, Y1) -> + wxDC:drawLine(DC, {round(X0), round(Y0)}, {round(X1), round(Y1)}); +strokeLine({true, GC}, X0, Y0, X1, Y1) -> + ?wxGC:strokeLine(GC, X0, Y0, X1, Y1). + +strokeLines({false, DC}, Lines) -> + wxDC:drawLines(DC, [{round(X), round(Y)} || {X,Y} <- Lines]); +strokeLines({true, GC}, Lines) -> + ?wxGC:strokeLines(GC, Lines). + +drawRoundedRectangle({false, DC}, X0, Y0, X1, Y1, R) -> + wxDC:drawRoundedRectangle(DC, {round(X0), round(Y0)}, {round(X1), round(Y1)}, round(R)); +drawRoundedRectangle({true, GC}, X0, Y0, X1, Y1, R) -> + ?wxGC:drawRoundedRectangle(GC, X0, Y0, X1, Y1, R). + +drawText({false, DC}, Str, X, Y) -> + wxDC:drawText(DC, Str, {round(X),round(Y)}); +drawText({true, GC}, Str, X, Y) -> + ?wxGC:drawText(GC, Str, X, Y). + +getTextExtent({false, DC}, Str) -> + wxDC:getTextExtent(DC, Str); +getTextExtent({true, GC}, Str) -> + {W,H,_,_} = ?wxGC:getTextExtent(GC, Str), + {W,H}. -- cgit v1.2.3 From f413a27b73d1e0ceaf0d31fc9615208f11645108 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 21 Feb 2012 11:43:08 +0100 Subject: [observer] Windows double buffer fixes DC's and GC's is not double buffered by default on windows, and there is a separate erase event which causes awful flickering when constant updating a window. Hack around wx (to be able to use wxBufferPaintDC), to avoid flickering on windows. This works on windows because there (and only there) wxGC:create/1 also takes a memoryDC as argument. --- lib/observer/priv/erlang_observer.png | Bin 4351 -> 2679 bytes lib/observer/src/observer_app_wx.erl | 20 +++++++++--- lib/observer/src/observer_perf_wx.erl | 59 ++++++++++++++++++++++------------ 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/lib/observer/priv/erlang_observer.png b/lib/observer/priv/erlang_observer.png index 78e70461b1..01723d210b 100644 Binary files a/lib/observer/priv/erlang_observer.png and b/lib/observer/priv/erlang_observer.png differ diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 900fba911a..7eac2b8fab 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -108,11 +108,18 @@ init([Notebook, Parent]) -> wxPanel:connect(DrawingArea, left_up), wxPanel:connect(DrawingArea, left_dclick), wxPanel:connect(DrawingArea, right_down), + case os:type() of + {win32, _} -> %% Ignore erase on windows + wxPanel:connect(DrawingArea, erase_background, [{callback, fun(_,_) -> ok end}]); + _ -> ok + end, UseGC = haveGC(DrawingArea), - Font = case UseGC of - true -> wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT); - false -> wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL) + Font = case os:type() of + {unix,_} when UseGC -> + wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL); + _ -> + wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT) end, SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), GreyBrush = wxBrush:new({230,230,240}), @@ -231,7 +238,12 @@ handle_event(Event, _State) -> handle_sync_event(#wx{event = #wxPaint{}},_, #state{app_w=DA, app=App, sel=Sel, paint=Paint, usegc=UseGC}) -> %% PaintDC must be created in a callback to work on windows. - DC = wxPaintDC:new(DA), + IsWindows = element(1, os:type()) =:= win32, + %% Avoid Windows flickering hack + DC = if IsWindows -> wx:typeCast(wxBufferedPaintDC:new(DA), wxPaintDC); + true -> wxPaintDC:new(DA) + end, + IsWindows andalso wxDC:clear(DC), GC = case UseGC of true -> GC0 = ?wxGC:create(DC), diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index c4659d1ea1..0de9785fb9 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -61,15 +61,15 @@ init([Notebook, Parent]) -> try Panel = wxPanel:new(Notebook), Main = wxBoxSizer:new(?wxVERTICAL), - - CPU = wxPanel:new(Panel, [{winid, ?RQ_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), + Style = ?wxFULL_REPAINT_ON_RESIZE bor ?wxCLIP_CHILDREN, + CPU = wxPanel:new(Panel, [{winid, ?RQ_W}, {style,Style}]), wxWindow:setBackgroundColour(CPU, ?wxWHITE), wxSizer:add(Main, CPU, [{flag, ?wxEXPAND bor ?wxALL}, {proportion, 1}, {border, 5}]), MemIO = wxBoxSizer:new(?wxHORIZONTAL), - MEM = wxPanel:new(Panel, [{winid, ?MEM_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), + MEM = wxPanel:new(Panel, [{winid, ?MEM_W}, {style,Style}]), wxWindow:setBackgroundColour(MEM, ?wxWHITE), - IO = wxPanel:new(Panel, [{winid, ?IO_W}, {style,?wxFULL_REPAINT_ON_RESIZE}]), + IO = wxPanel:new(Panel, [{winid, ?IO_W}, {style,Style}]), wxWindow:setBackgroundColour(IO, ?wxWHITE), wxSizer:add(MemIO, MEM, [{flag, ?wxEXPAND bor ?wxLEFT}, {proportion, 1}, {border, 5}]), @@ -82,20 +82,31 @@ init([Notebook, Parent]) -> wxPanel:connect(CPU, paint, [callback]), wxPanel:connect(IO, paint, [callback]), wxPanel:connect(MEM, paint, [callback]), + case os:type() of + {win32, _} -> %% Ignore erase on windows + wxPanel:connect(CPU, erase_background, [{callback, fun(_,_) -> ok end}]), + wxPanel:connect(IO, erase_background, [{callback, fun(_,_) -> ok end}]), + wxPanel:connect(MEM, erase_background, [{callback, fun(_,_) -> ok end}]); + _ -> ok + end, UseGC = haveGC(Panel), - Font = case UseGC of - true -> - %% Def font is really small when using Graphics contexts for some reason - %% Hardcode it - wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD); - false -> - DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), - DefSize = wxFont:getPointSize(DefFont), - DefFamily = wxFont:getFamily(DefFont), - wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD) - end, - SmallFont = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + {Font, SmallFont} + = case os:type() of + {unix, _} when UseGC -> + %% Def font is really small when using Graphics contexts for some reason + %% Hardcode it + F = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), + SF = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + {F, SF}; + _ -> + DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + DefSize = wxFont:getPointSize(DefFont), + DefFamily = wxFont:getFamily(DefFont), + F = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), + SF = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + {F, SF} + end, BlackPen = wxPen:new({0,0,0}, [{width, 2}]), Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], process_flag(trap_exit, true), @@ -135,10 +146,18 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, Panel =:= element(?MEM_W, Windows) -> ?MEM_W; Panel =:= element(?IO_W, Windows) -> ?IO_W end, - DC = wxPaintDC:new(Panel), - GC = case UseGC of - true -> ?wxGC:create(DC); - false -> DC + IsWindows = element(1, os:type()) =:= win32, + + DC = if IsWindows -> + %% Ugly hack to aviod flickering on windows, works on windows only + %% But the other platforms are doublebuffered by default + wx:typeCast(wxBufferedPaintDC:new(Panel), wxPaintDC); + true -> + wxPaintDC:new(Panel) + end, + IsWindows andalso wxDC:clear(DC), + GC = if UseGC -> ?wxGC:create(DC); + true -> DC end, %% Nothing is drawn until wxPaintDC is destroyed. try -- cgit v1.2.3