%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% -module(observer_alloc_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"). -record(state, { offset = 0.0, active = false, parent, windows, data = {0, queue:new()}, panel, paint, appmon, async }). -define(ALLOC_W, 1). -define(UTIL_W, 2). start_link(Notebook, Parent) -> wx_object:start_link(?MODULE, [Notebook, Parent], []). init([Notebook, Parent]) -> try Panel = wxPanel:new(Notebook), Main = wxBoxSizer:new(?wxVERTICAL), Style = ?wxFULL_REPAINT_ON_RESIZE bor ?wxCLIP_CHILDREN, Carrier = wxPanel:new(Panel, [{winid, ?ALLOC_W}, {style,Style}]), Utilz = wxPanel:new(Panel, [{winid, ?UTIL_W}, {style,Style}]), BorderFlags = ?wxLEFT bor ?wxRIGHT, wxSizer:add(Main, Carrier, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, {proportion, 1}, {border, 5}]), wxSizer:add(Main, Utilz, [{flag, ?wxEXPAND bor BorderFlags}, {proportion, 1}, {border, 5}]), MemWin = {MemPanel,_} = create_mem_info(Panel), wxSizer:add(Main, MemPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM}, {proportion, 1}, {border, 5}]), wxWindow:setSizer(Panel, Main), PaintInfo = observer_perf_wx:setup_graph_drawing([Carrier, Utilz]), {Panel, #state{parent=Parent, panel =Panel, windows = {Carrier, Utilz, MemWin}, paint=PaintInfo} } catch _:Err -> io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), {stop, Err} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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{obj=Panel, event = #wxPaint{}},_, #state{active=Active, offset=Offset, paint=Paint, windows=Windows, data=Data}) -> %% Sigh workaround bug on MacOSX (Id in paint event is always 0) Id = if Panel =:= element(?ALLOC_W, Windows) -> alloc; Panel =:= element(?UTIL_W, Windows) -> utilz end, observer_perf_wx:refresh_panel(Panel, Id, Offset, Data, Active, Paint), ok. %%%%%%%%%% handle_call(Event, From, _State) -> error({unhandled_call, Event, From}). handle_cast(Event, _State) -> error({unhandled_cast, Event}). %%%%%%%%%% handle_info({Key, {promise_reply, {badrpc, _}}}, #state{async=Key} = State) -> {noreply, State#state{active=false, appmon=undefined}}; handle_info({Key, {promise_reply, SysInfo}}, #state{async=Key, data=Data} = State) -> Info = alloc_info(SysInfo), update_alloc(State, Info), {noreply, State#state{offset=0.0, data = add_data(Info, Data), async=undefined}}; handle_info({refresh, Seq, Freq, Node}, #state{panel=Panel, appmon=Node, async=Key} = State) -> wxWindow:refresh(Panel), Next = Seq+1, if Next > Freq, Key =:= undefined -> erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq, Node}), Req = rpc:async_call(Node, observer_backend, sys_info, []), {noreply, State#state{offset=Seq/Freq, async=Req}}; true -> erlang:send_after(trunc(1000 / Freq), self(), {refresh, Next, Freq, Node}), {noreply, State#state{offset=Seq/Freq}} end; handle_info({refresh, _Seq, _Freq, _Node}, State) -> {noreply, State}; handle_info({active, Node}, State = #state{parent=Parent, panel=Panel, appmon=Old}) -> create_menus(Parent, []), try Node = Old, wxWindow:refresh(Panel), {noreply, State#state{active=true}} catch _:_ -> SysInfo = observer_wx:try_rpc(Node, observer_backend, sys_info, []), Info = alloc_info(SysInfo), Freq = 6, erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq, Node}), wxWindow:refresh(Panel), {noreply, State#state{active=true, appmon=Node, offset=0.0, data = add_data(Info, {0, queue:new()})}} end; handle_info(not_active, State = #state{appmon=_Pid}) -> {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{}) -> 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)}. update_alloc(#state{windows={_, _, {_, Grid}}}, Fields) -> Max = wxListCtrl:getItemCount(Grid), Update = fun({Name, BS, CS}, Row) -> (Row >= Max) andalso wxListCtrl:insertItem(Grid, Row, ""), wxListCtrl:setItem(Grid, Row, 0, observer_lib:to_str(Name)), wxListCtrl:setItem(Grid, Row, 1, observer_lib:to_str(BS div 1024)), wxListCtrl:setItem(Grid, Row, 2, observer_lib:to_str(CS div 1024)), Row + 1 end, lists:foldl(Update, 0, Fields), Fields. alloc_info(SysInfo) -> AllocInfo = proplists:get_value(alloc_info, SysInfo, []), alloc_info(AllocInfo, [], 0, 0, true). alloc_info([{Type,Instances}|Allocators],TypeAcc,TotalBS,TotalCS,IncludeTotal) -> {BS,CS,NewTotalBS,NewTotalCS,NewIncludeTotal} = sum_alloc_instances(Instances,0,0,TotalBS,TotalCS), alloc_info(Allocators,[{Type,BS,CS}|TypeAcc],NewTotalBS,NewTotalCS, IncludeTotal andalso NewIncludeTotal); alloc_info([],TypeAcc,TotalBS,TotalCS,IncludeTotal) -> Types = [X || X={_,BS,CS} <- TypeAcc, (BS>0 orelse CS>0)], case IncludeTotal of true -> [{total,TotalBS,TotalCS} | lists:reverse(Types)]; false -> lists:reverse(Types) end. sum_alloc_instances(false,BS,CS,TotalBS,TotalCS) -> {BS,CS,TotalBS,TotalCS,false}; sum_alloc_instances([{_,_,Data}|Instances],BS,CS,TotalBS,TotalCS) -> {NewBS,NewCS,NewTotalBS,NewTotalCS} = sum_alloc_one_instance(Data,BS,CS,TotalBS,TotalCS), sum_alloc_instances(Instances,NewBS,NewCS,NewTotalBS,NewTotalCS); sum_alloc_instances([],BS,CS,TotalBS,TotalCS) -> {BS,CS,TotalBS,TotalCS,true}. sum_alloc_one_instance([{sbmbcs,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}| Rest],OldBS,OldCS,TotalBS,TotalCS) -> sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS,TotalCS); sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}| Rest],OldBS,OldCS,TotalBS,TotalCS) -> sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS); sum_alloc_one_instance([{_,[{blocks_size,BS},{carriers_size,CS}]}| Rest],OldBS,OldCS,TotalBS,TotalCS) -> sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS); sum_alloc_one_instance([_|Rest],BS,CS,TotalBS,TotalCS) -> sum_alloc_one_instance(Rest,BS,CS,TotalBS,TotalCS); sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) -> {BS,CS,TotalBS,TotalCS}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% create_mem_info(Parent) -> Panel = wxPanel:new(Parent), wxWindow:setBackgroundColour(Panel, {255,255,255}), Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES, Grid = wxListCtrl:new(Panel, [{style, Style}]), Li = wxListItem:new(), AddListEntry = fun({Name, Align, DefSize}, Col) -> wxListItem:setText(Li, Name), wxListItem:setAlign(Li, Align), wxListCtrl:insertColumn(Grid, Col, Li), wxListCtrl:setColumnWidth(Grid, Col, DefSize), Col + 1 end, ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200}, {"Block size (kB)", ?wxLIST_FORMAT_RIGHT, 150}, {"Carrier size (kB)",?wxLIST_FORMAT_RIGHT, 150}], lists:foldl(AddListEntry, 0, ListItems), wxListItem:destroy(Li), Sizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT}, {border, 5}, {proportion, 1}]), wxWindow:setSizerAndFit(Panel, Sizer), {Panel, Grid}. create_menus(Parent, _) -> MenuEntries = [{"File", [ ]} ], observer_wx:create_menus(Parent, MenuEntries). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%