1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(gen_server). 21 22%%% 23%%% NOTE: If init_ack() return values are modified, see comment 24%%% above monitor_return() in gen.erl! 25%%% 26 27%%% --------------------------------------------------- 28%%% 29%%% The idea behind THIS server is that the user module 30%%% provides (different) functions to handle different 31%%% kind of inputs. 32%%% If the Parent process terminates the Module:terminate/2 33%%% function is called. 34%%% 35%%% The user module should export: 36%%% 37%%% init(Args) 38%%% ==> {ok, State} 39%%% {ok, State, Timeout} 40%%% ignore 41%%% {stop, Reason} 42%%% 43%%% handle_call(Msg, {From, Tag}, State) 44%%% 45%%% ==> {reply, Reply, State} 46%%% {reply, Reply, State, Timeout} 47%%% {noreply, State} 48%%% {noreply, State, Timeout} 49%%% {stop, Reason, Reply, State} 50%%% Reason = normal | shutdown | Term terminate(State) is called 51%%% 52%%% handle_cast(Msg, State) 53%%% 54%%% ==> {noreply, State} 55%%% {noreply, State, Timeout} 56%%% {stop, Reason, State} 57%%% Reason = normal | shutdown | Term terminate(State) is called 58%%% 59%%% handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ... 60%%% 61%%% ==> {noreply, State} 62%%% {noreply, State, Timeout} 63%%% {stop, Reason, State} 64%%% Reason = normal | shutdown | Term, terminate(State) is called 65%%% 66%%% terminate(Reason, State) Let the user module clean up 67%%% always called when server terminates 68%%% 69%%% ==> ok 70%%% 71%%% 72%%% The work flow (of the server) can be described as follows: 73%%% 74%%% User module Generic 75%%% ----------- ------- 76%%% start -----> start 77%%% init <----- . 78%%% 79%%% loop 80%%% handle_call <----- . 81%%% -----> reply 82%%% 83%%% handle_cast <----- . 84%%% 85%%% handle_info <----- . 86%%% 87%%% terminate <----- . 88%%% 89%%% -----> reply 90%%% 91%%% 92%%% --------------------------------------------------- 93 94%% API 95-export([start/3, start/4, 96 start_link/3, start_link/4, 97 start_monitor/3, start_monitor/4, 98 stop/1, stop/3, 99 call/2, call/3, 100 send_request/2, wait_response/2, 101 receive_response/2, check_response/2, 102 cast/2, reply/2, 103 abcast/2, abcast/3, 104 multi_call/2, multi_call/3, multi_call/4, 105 enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/6]). 106 107%% System exports 108-export([system_continue/3, 109 system_terminate/4, 110 system_code_change/4, 111 system_get_state/1, 112 system_replace_state/2, 113 format_status/2]). 114 115%% logger callback 116-export([format_log/1, format_log/2]). 117 118%% Internal exports 119-export([init_it/6]). 120 121-include("logger.hrl"). 122 123-define( 124 STACKTRACE(), 125 element(2, erlang:process_info(self(), current_stacktrace))). 126 127 128-type server_ref() :: 129 pid() 130 | (LocalName :: atom()) 131 | {Name :: atom(), Node :: atom()} 132 | {'global', GlobalName :: term()} 133 | {'via', RegMod :: module(), ViaName :: term()}. 134 135-type request_id() :: term(). 136 137%%%========================================================================= 138%%% API 139%%%========================================================================= 140 141-callback init(Args :: term()) -> 142 {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate | {continue, term()}} | 143 {stop, Reason :: term()} | ignore. 144-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, 145 State :: term()) -> 146 {reply, Reply :: term(), NewState :: term()} | 147 {reply, Reply :: term(), NewState :: term(), timeout() | hibernate | {continue, term()}} | 148 {noreply, NewState :: term()} | 149 {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | 150 {stop, Reason :: term(), Reply :: term(), NewState :: term()} | 151 {stop, Reason :: term(), NewState :: term()}. 152-callback handle_cast(Request :: term(), State :: term()) -> 153 {noreply, NewState :: term()} | 154 {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | 155 {stop, Reason :: term(), NewState :: term()}. 156-callback handle_info(Info :: timeout | term(), State :: term()) -> 157 {noreply, NewState :: term()} | 158 {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | 159 {stop, Reason :: term(), NewState :: term()}. 160-callback handle_continue(Info :: term(), State :: term()) -> 161 {noreply, NewState :: term()} | 162 {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | 163 {stop, Reason :: term(), NewState :: term()}. 164-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | 165 term()), 166 State :: term()) -> 167 term(). 168-callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), 169 Extra :: term()) -> 170 {ok, NewState :: term()} | {error, Reason :: term()}. 171-callback format_status(Opt, StatusData) -> Status when 172 Opt :: 'normal' | 'terminate', 173 StatusData :: [PDict | State], 174 PDict :: [{Key :: term(), Value :: term()}], 175 State :: term(), 176 Status :: term(). 177 178-optional_callbacks( 179 [handle_info/2, handle_continue/2, terminate/2, code_change/3, format_status/2]). 180 181%%% ----------------------------------------------------------------- 182%%% Starts a generic server. 183%%% start(Mod, Args, Options) 184%%% start(Name, Mod, Args, Options) 185%%% start_link(Mod, Args, Options) 186%%% start_link(Name, Mod, Args, Options) where: 187%%% Name ::= {local, atom()} | {global, term()} | {via, atom(), term()} 188%%% Mod ::= atom(), callback module implementing the 'real' server 189%%% Args ::= term(), init arguments (to Mod:init/1) 190%%% Options ::= [{timeout, Timeout} | {debug, [Flag]}] 191%%% Flag ::= trace | log | {logfile, File} | statistics | debug 192%%% (debug == log && statistics) 193%%% Returns: {ok, Pid} | 194%%% {error, {already_started, Pid}} | 195%%% {error, Reason} 196%%% ----------------------------------------------------------------- 197start(Mod, Args, Options) -> 198 gen:start(?MODULE, nolink, Mod, Args, Options). 199 200start(Name, Mod, Args, Options) -> 201 gen:start(?MODULE, nolink, Name, Mod, Args, Options). 202 203start_link(Mod, Args, Options) -> 204 gen:start(?MODULE, link, Mod, Args, Options). 205 206start_link(Name, Mod, Args, Options) -> 207 gen:start(?MODULE, link, Name, Mod, Args, Options). 208 209start_monitor(Mod, Args, Options) -> 210 gen:start(?MODULE, monitor, Mod, Args, Options). 211 212start_monitor(Name, Mod, Args, Options) -> 213 gen:start(?MODULE, monitor, Name, Mod, Args, Options). 214 215 216%% ----------------------------------------------------------------- 217%% Stop a generic server and wait for it to terminate. 218%% If the server is located at another node, that node will 219%% be monitored. 220%% ----------------------------------------------------------------- 221stop(Name) -> 222 gen:stop(Name). 223 224stop(Name, Reason, Timeout) -> 225 gen:stop(Name, Reason, Timeout). 226 227%% ----------------------------------------------------------------- 228%% Make a call to a generic server. 229%% If the server is located at another node, that node will 230%% be monitored. 231%% If the client is trapping exits and is linked server termination 232%% is handled here (? Shall we do that here (or rely on timeouts) ?). 233%% ----------------------------------------------------------------- 234call(Name, Request) -> 235 case catch gen:call(Name, '$gen_call', Request) of 236 {ok,Res} -> 237 Res; 238 {'EXIT',Reason} -> 239 exit({Reason, {?MODULE, call, [Name, Request]}}) 240 end. 241 242call(Name, Request, Timeout) -> 243 case catch gen:call(Name, '$gen_call', Request, Timeout) of 244 {ok,Res} -> 245 Res; 246 {'EXIT',Reason} -> 247 exit({Reason, {?MODULE, call, [Name, Request, Timeout]}}) 248 end. 249 250%% ----------------------------------------------------------------- 251%% Send a request to a generic server and return a Key which should be 252%% used with wait_response/2 or check_response/2 to fetch the 253%% result of the request. 254 255-spec send_request(Name::server_ref(), Request::term()) -> request_id(). 256send_request(Name, Request) -> 257 gen:send_request(Name, '$gen_call', Request). 258 259-spec wait_response(RequestId::request_id(), timeout()) -> 260 {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), server_ref()}}. 261wait_response(RequestId, Timeout) -> 262 gen:wait_response(RequestId, Timeout). 263 264-spec receive_response(RequestId::request_id(), timeout()) -> 265 {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), server_ref()}}. 266receive_response(RequestId, Timeout) -> 267 gen:receive_response(RequestId, Timeout). 268 269-spec check_response(Msg::term(), RequestId::request_id()) -> 270 {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), server_ref()}}. 271check_response(Msg, RequestId) -> 272 gen:check_response(Msg, RequestId). 273 274%% ----------------------------------------------------------------- 275%% Make a cast to a generic server. 276%% ----------------------------------------------------------------- 277cast({global,Name}, Request) -> 278 catch global:send(Name, cast_msg(Request)), 279 ok; 280cast({via, Mod, Name}, Request) -> 281 catch Mod:send(Name, cast_msg(Request)), 282 ok; 283cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) -> 284 do_cast(Dest, Request); 285cast(Dest, Request) when is_atom(Dest) -> 286 do_cast(Dest, Request); 287cast(Dest, Request) when is_pid(Dest) -> 288 do_cast(Dest, Request). 289 290do_cast(Dest, Request) -> 291 do_send(Dest, cast_msg(Request)), 292 ok. 293 294cast_msg(Request) -> {'$gen_cast',Request}. 295 296%% ----------------------------------------------------------------- 297%% Send a reply to the client. 298%% ----------------------------------------------------------------- 299reply(From, Reply) -> 300 gen:reply(From, Reply). 301 302%% ----------------------------------------------------------------- 303%% Asynchronous broadcast, returns nothing, it's just send 'n' pray 304%%----------------------------------------------------------------- 305abcast(Name, Request) when is_atom(Name) -> 306 do_abcast([node() | nodes()], Name, cast_msg(Request)). 307 308abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) -> 309 do_abcast(Nodes, Name, cast_msg(Request)). 310 311do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) -> 312 do_send({Name,Node},Msg), 313 do_abcast(Nodes, Name, Msg); 314do_abcast([], _,_) -> abcast. 315 316%%% ----------------------------------------------------------------- 317%%% Make a call to servers at several nodes. 318%%% Returns: {[Replies],[BadNodes]} 319%%% A Timeout can be given 320%%% 321%%% A middleman process is used in case late answers arrives after 322%%% the timeout. If they would be allowed to glog the callers message 323%%% queue, it would probably become confused. Late answers will 324%%% now arrive to the terminated middleman and so be discarded. 325%%% ----------------------------------------------------------------- 326multi_call(Name, Req) 327 when is_atom(Name) -> 328 do_multi_call([node() | nodes()], Name, Req, infinity). 329 330multi_call(Nodes, Name, Req) 331 when is_list(Nodes), is_atom(Name) -> 332 do_multi_call(Nodes, Name, Req, infinity). 333 334multi_call(Nodes, Name, Req, infinity) -> 335 do_multi_call(Nodes, Name, Req, infinity); 336multi_call(Nodes, Name, Req, Timeout) 337 when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 -> 338 do_multi_call(Nodes, Name, Req, Timeout). 339 340 341%%----------------------------------------------------------------- 342%% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>) ->_ 343%% 344%% Description: Makes an existing process into a gen_server. 345%% The calling process will enter the gen_server receive 346%% loop and become a gen_server process. 347%% The process *must* have been started using one of the 348%% start functions in proc_lib, see proc_lib(3). 349%% The user is responsible for any initialization of the 350%% process, including registering a name for it. 351%%----------------------------------------------------------------- 352enter_loop(Mod, Options, State) -> 353 enter_loop(Mod, Options, State, self(), infinity). 354 355enter_loop(Mod, Options, State, ServerName = {Scope, _}) 356 when Scope == local; Scope == global -> 357 enter_loop(Mod, Options, State, ServerName, infinity); 358 359enter_loop(Mod, Options, State, ServerName = {via, _, _}) -> 360 enter_loop(Mod, Options, State, ServerName, infinity); 361 362enter_loop(Mod, Options, State, Timeout) -> 363 enter_loop(Mod, Options, State, self(), Timeout). 364 365enter_loop(Mod, Options, State, ServerName, Timeout) -> 366 Name = gen:get_proc_name(ServerName), 367 Parent = gen:get_parent(), 368 Debug = gen:debug_options(Name, Options), 369 HibernateAfterTimeout = gen:hibernate_after(Options), 370 loop(Parent, Name, State, Mod, Timeout, HibernateAfterTimeout, Debug). 371 372%%%======================================================================== 373%%% Gen-callback functions 374%%%======================================================================== 375 376%%% --------------------------------------------------- 377%%% Initiate the new process. 378%%% Register the name using the Rfunc function 379%%% Calls the Mod:init/Args function. 380%%% Finally an acknowledge is sent to Parent and the main 381%%% loop is entered. 382%%% --------------------------------------------------- 383init_it(Starter, self, Name, Mod, Args, Options) -> 384 init_it(Starter, self(), Name, Mod, Args, Options); 385init_it(Starter, Parent, Name0, Mod, Args, Options) -> 386 Name = gen:name(Name0), 387 Debug = gen:debug_options(Name, Options), 388 HibernateAfterTimeout = gen:hibernate_after(Options), 389 390 case init_it(Mod, Args) of 391 {ok, {ok, State}} -> 392 proc_lib:init_ack(Starter, {ok, self()}), 393 loop(Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug); 394 {ok, {ok, State, TimeoutHibernateOrContinue}} -> 395 proc_lib:init_ack(Starter, {ok, self()}), 396 loop(Parent, Name, State, Mod, TimeoutHibernateOrContinue, 397 HibernateAfterTimeout, Debug); 398 {ok, {stop, Reason}} -> 399 %% For consistency, we must make sure that the 400 %% registered name (if any) is unregistered before 401 %% the parent process is notified about the failure. 402 %% (Otherwise, the parent process could get 403 %% an 'already_started' error if it immediately 404 %% tried starting the process again.) 405 gen:unregister_name(Name0), 406 proc_lib:init_ack(Starter, {error, Reason}), 407 exit(Reason); 408 {ok, ignore} -> 409 gen:unregister_name(Name0), 410 proc_lib:init_ack(Starter, ignore), 411 exit(normal); 412 {ok, Else} -> 413 Error = {bad_return_value, Else}, 414 proc_lib:init_ack(Starter, {error, Error}), 415 exit(Error); 416 {'EXIT', Class, Reason, Stacktrace} -> 417 gen:unregister_name(Name0), 418 proc_lib:init_ack(Starter, {error, terminate_reason(Class, Reason, Stacktrace)}), 419 erlang:raise(Class, Reason, Stacktrace) 420 end. 421init_it(Mod, Args) -> 422 try 423 {ok, Mod:init(Args)} 424 catch 425 throw:R -> {ok, R}; 426 Class:R:S -> {'EXIT', Class, R, S} 427 end. 428 429%%%======================================================================== 430%%% Internal functions 431%%%======================================================================== 432%%% --------------------------------------------------- 433%%% The MAIN loop. 434%%% --------------------------------------------------- 435 436loop(Parent, Name, State, Mod, {continue, Continue} = Msg, HibernateAfterTimeout, Debug) -> 437 Reply = try_dispatch(Mod, handle_continue, Continue, State), 438 case Debug of 439 [] -> 440 handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, 441 HibernateAfterTimeout, State); 442 _ -> 443 Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, Msg), 444 handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, 445 HibernateAfterTimeout, State, Debug1) 446 end; 447 448loop(Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug) -> 449 proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, HibernateAfterTimeout, Debug]); 450 451loop(Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug) -> 452 receive 453 Msg -> 454 decode_msg(Msg, Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug, false) 455 after HibernateAfterTimeout -> 456 loop(Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug) 457 end; 458 459loop(Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug) -> 460 Msg = receive 461 Input -> 462 Input 463 after Time -> 464 timeout 465 end, 466 decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, false). 467 468wake_hib(Parent, Name, State, Mod, HibernateAfterTimeout, Debug) -> 469 Msg = receive 470 Input -> 471 Input 472 end, 473 decode_msg(Msg, Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug, true). 474 475decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hib) -> 476 case Msg of 477 {system, From, Req} -> 478 sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, 479 [Name, State, Mod, Time, HibernateAfterTimeout], Hib); 480 {'EXIT', Parent, Reason} -> 481 terminate(Reason, ?STACKTRACE(), Name, undefined, Msg, Mod, State, Debug); 482 _Msg when Debug =:= [] -> 483 handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout); 484 _Msg -> 485 Debug1 = sys:handle_debug(Debug, fun print_event/3, 486 Name, {in, Msg}), 487 handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug1) 488 end. 489 490%%% --------------------------------------------------- 491%%% Send/receive functions 492%%% --------------------------------------------------- 493do_send(Dest, Msg) -> 494 try erlang:send(Dest, Msg) 495 catch 496 error:_ -> ok 497 end, 498 ok. 499 500do_multi_call([Node], Name, Req, infinity) when Node =:= node() -> 501 % Special case when multi_call is used with local node only. 502 % In that case we can leverage the benefit of recv_mark optimisation 503 % existing in simple gen:call. 504 try gen:call(Name, '$gen_call', Req, infinity) of 505 {ok, Res} -> {[{Node, Res}],[]} 506 catch exit:_ -> 507 {[], [Node]} 508 end; 509do_multi_call(Nodes, Name, Req, infinity) -> 510 Tag = make_ref(), 511 Monitors = send_nodes(Nodes, Name, Tag, Req), 512 rec_nodes(Tag, Monitors, Name, undefined); 513do_multi_call(Nodes, Name, Req, Timeout) -> 514 Tag = make_ref(), 515 Caller = self(), 516 Receiver = 517 spawn( 518 fun() -> 519 %% Middleman process. Should be unsensitive to regular 520 %% exit signals. The sychronization is needed in case 521 %% the receiver would exit before the caller started 522 %% the monitor. 523 process_flag(trap_exit, true), 524 Mref = erlang:monitor(process, Caller), 525 receive 526 {Caller,Tag} -> 527 Monitors = send_nodes(Nodes, Name, Tag, Req), 528 TimerId = erlang:start_timer(Timeout, self(), ok), 529 Result = rec_nodes(Tag, Monitors, Name, TimerId), 530 exit({self(),Tag,Result}); 531 {'DOWN',Mref,_,_,_} -> 532 %% Caller died before sending us the go-ahead. 533 %% Give up silently. 534 exit(normal) 535 end 536 end), 537 Mref = erlang:monitor(process, Receiver), 538 Receiver ! {self(),Tag}, 539 receive 540 {'DOWN',Mref,_,_,{Receiver,Tag,Result}} -> 541 Result; 542 {'DOWN',Mref,_,_,Reason} -> 543 %% The middleman code failed. Or someone did 544 %% exit(_, kill) on the middleman process => Reason==killed 545 exit(Reason) 546 end. 547 548send_nodes(Nodes, Name, Tag, Req) -> 549 send_nodes(Nodes, Name, Tag, Req, []). 550 551send_nodes([Node|Tail], Name, Tag, Req, Monitors) 552 when is_atom(Node) -> 553 Monitor = start_monitor(Node, Name), 554 %% Handle non-existing names in rec_nodes. 555 catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req}, 556 send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]); 557send_nodes([_Node|Tail], Name, Tag, Req, Monitors) -> 558 %% Skip non-atom Node 559 send_nodes(Tail, Name, Tag, Req, Monitors); 560send_nodes([], _Name, _Tag, _Req, Monitors) -> 561 Monitors. 562 563%% Against old nodes: 564%% If no reply has been delivered within 2 secs. (per node) check that 565%% the server really exists and wait for ever for the answer. 566%% 567%% Against contemporary nodes: 568%% Wait for reply, server 'DOWN', or timeout from TimerId. 569 570rec_nodes(Tag, Nodes, Name, TimerId) -> 571 rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId). 572 573rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) -> 574 receive 575 {'DOWN', R, _, _, _} -> 576 rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId); 577 {{Tag, N}, Reply} -> %% Tag is bound !!! 578 erlang:demonitor(R, [flush]), 579 rec_nodes(Tag, Tail, Name, Badnodes, 580 [{N,Reply}|Replies], Time, TimerId); 581 {timeout, TimerId, _} -> 582 erlang:demonitor(R, [flush]), 583 %% Collect all replies that already have arrived 584 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) 585 end; 586rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) -> 587 %% R6 node 588 receive 589 {nodedown, N} -> 590 monitor_node(N, false), 591 rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId); 592 {{Tag, N}, Reply} -> %% Tag is bound !!! 593 receive {nodedown, N} -> ok after 0 -> ok end, 594 monitor_node(N, false), 595 rec_nodes(Tag, Tail, Name, Badnodes, 596 [{N,Reply}|Replies], 2000, TimerId); 597 {timeout, TimerId, _} -> 598 receive {nodedown, N} -> ok after 0 -> ok end, 599 monitor_node(N, false), 600 %% Collect all replies that already have arrived 601 rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies) 602 after Time -> 603 case rpc:call(N, erlang, whereis, [Name]) of 604 Pid when is_pid(Pid) -> % It exists try again. 605 rec_nodes(Tag, [N|Tail], Name, Badnodes, 606 Replies, infinity, TimerId); 607 _ -> % badnode 608 receive {nodedown, N} -> ok after 0 -> ok end, 609 monitor_node(N, false), 610 rec_nodes(Tag, Tail, Name, [N|Badnodes], 611 Replies, 2000, TimerId) 612 end 613 end; 614rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) -> 615 case catch erlang:cancel_timer(TimerId) of 616 false -> % It has already sent it's message 617 receive 618 {timeout, TimerId, _} -> ok 619 after 0 -> 620 ok 621 end; 622 _ -> % Timer was cancelled, or TimerId was 'undefined' 623 ok 624 end, 625 {Replies, Badnodes}. 626 627%% Collect all replies that already have arrived 628rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) -> 629 receive 630 {'DOWN', R, _, _, _} -> 631 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); 632 {{Tag, N}, Reply} -> %% Tag is bound !!! 633 erlang:demonitor(R, [flush]), 634 rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) 635 after 0 -> 636 erlang:demonitor(R, [flush]), 637 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) 638 end; 639rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) -> 640 %% R6 node 641 receive 642 {nodedown, N} -> 643 monitor_node(N, false), 644 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); 645 {{Tag, N}, Reply} -> %% Tag is bound !!! 646 receive {nodedown, N} -> ok after 0 -> ok end, 647 monitor_node(N, false), 648 rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) 649 after 0 -> 650 receive {nodedown, N} -> ok after 0 -> ok end, 651 monitor_node(N, false), 652 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) 653 end; 654rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) -> 655 {Replies, Badnodes}. 656 657 658%%% --------------------------------------------------- 659%%% Monitor functions 660%%% --------------------------------------------------- 661 662start_monitor(Node, Name) when is_atom(Node), is_atom(Name) -> 663 if node() =:= nonode@nohost, Node =/= nonode@nohost -> 664 Ref = make_ref(), 665 self() ! {'DOWN', Ref, process, {Name, Node}, noconnection}, 666 {Node, Ref}; 667 true -> 668 case catch erlang:monitor(process, {Name, Node}) of 669 {'EXIT', _} -> 670 %% Remote node is R6 671 monitor_node(Node, true), 672 Node; 673 Ref when is_reference(Ref) -> 674 {Node, Ref} 675 end 676 end. 677 678%% --------------------------------------------------- 679%% Helper functions for try-catch of callbacks. 680%% Returns the return value of the callback, or 681%% {'EXIT', Class, Reason, Stack} (if an exception occurs) 682%% 683%% The Class, Reason and Stack are given to erlang:raise/3 684%% to make sure proc_lib receives the proper reasons and 685%% stacktraces. 686%% --------------------------------------------------- 687 688try_dispatch({'$gen_cast', Msg}, Mod, State) -> 689 try_dispatch(Mod, handle_cast, Msg, State); 690try_dispatch(Info, Mod, State) -> 691 try_dispatch(Mod, handle_info, Info, State). 692 693try_dispatch(Mod, Func, Msg, State) -> 694 try 695 {ok, Mod:Func(Msg, State)} 696 catch 697 throw:R -> 698 {ok, R}; 699 error:undef = R:Stacktrace when Func == handle_info -> 700 case erlang:function_exported(Mod, handle_info, 2) of 701 false -> 702 ?LOG_WARNING( 703 #{label=>{gen_server,no_handle_info}, 704 module=>Mod, 705 message=>Msg}, 706 #{domain=>[otp], 707 report_cb=>fun gen_server:format_log/2, 708 error_logger=> 709 #{tag=>warning_msg, 710 report_cb=>fun gen_server:format_log/1}}), 711 {ok, {noreply, State}}; 712 true -> 713 {'EXIT', error, R, Stacktrace} 714 end; 715 Class:R:Stacktrace -> 716 {'EXIT', Class, R, Stacktrace} 717 end. 718 719try_handle_call(Mod, Msg, From, State) -> 720 try 721 {ok, Mod:handle_call(Msg, From, State)} 722 catch 723 throw:R -> 724 {ok, R}; 725 Class:R:Stacktrace -> 726 {'EXIT', Class, R, Stacktrace} 727 end. 728 729try_terminate(Mod, Reason, State) -> 730 case erlang:function_exported(Mod, terminate, 2) of 731 true -> 732 try 733 {ok, Mod:terminate(Reason, State)} 734 catch 735 throw:R -> 736 {ok, R}; 737 Class:R:Stacktrace -> 738 {'EXIT', Class, R, Stacktrace} 739 end; 740 false -> 741 {ok, ok} 742 end. 743 744 745%%% --------------------------------------------------- 746%%% Message handling functions 747%%% --------------------------------------------------- 748 749handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, HibernateAfterTimeout) -> 750 Result = try_handle_call(Mod, Msg, From, State), 751 case Result of 752 {ok, {reply, Reply, NState}} -> 753 reply(From, Reply), 754 loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []); 755 {ok, {reply, Reply, NState, Time1}} -> 756 reply(From, Reply), 757 loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []); 758 {ok, {noreply, NState}} -> 759 loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []); 760 {ok, {noreply, NState, Time1}} -> 761 loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []); 762 {ok, {stop, Reason, Reply, NState}} -> 763 try 764 terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, []) 765 after 766 reply(From, Reply) 767 end; 768 Other -> handle_common_reply(Other, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State) 769 end; 770handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout) -> 771 Reply = try_dispatch(Msg, Mod, State), 772 handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, HibernateAfterTimeout, State). 773 774handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, HibernateAfterTimeout, Debug) -> 775 Result = try_handle_call(Mod, Msg, From, State), 776 case Result of 777 {ok, {reply, Reply, NState}} -> 778 Debug1 = reply(Name, From, Reply, NState, Debug), 779 loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1); 780 {ok, {reply, Reply, NState, Time1}} -> 781 Debug1 = reply(Name, From, Reply, NState, Debug), 782 loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1); 783 {ok, {noreply, NState}} -> 784 Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, 785 {noreply, NState}), 786 loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1); 787 {ok, {noreply, NState, Time1}} -> 788 Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, 789 {noreply, NState}), 790 loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1); 791 {ok, {stop, Reason, Reply, NState}} -> 792 try 793 terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, Debug) 794 after 795 _ = reply(Name, From, Reply, NState, Debug) 796 end; 797 Other -> 798 handle_common_reply(Other, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State, Debug) 799 end; 800handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug) -> 801 Reply = try_dispatch(Msg, Mod, State), 802 handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, HibernateAfterTimeout, State, Debug). 803 804handle_common_reply(Reply, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State) -> 805 case Reply of 806 {ok, {noreply, NState}} -> 807 loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []); 808 {ok, {noreply, NState, Time1}} -> 809 loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []); 810 {ok, {stop, Reason, NState}} -> 811 terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, []); 812 {'EXIT', Class, Reason, Stacktrace} -> 813 terminate(Class, Reason, Stacktrace, Name, From, Msg, Mod, State, []); 814 {ok, BadReply} -> 815 terminate({bad_return_value, BadReply}, ?STACKTRACE(), Name, From, Msg, Mod, State, []) 816 end. 817 818handle_common_reply(Reply, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State, Debug) -> 819 case Reply of 820 {ok, {noreply, NState}} -> 821 Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, 822 {noreply, NState}), 823 loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1); 824 {ok, {noreply, NState, Time1}} -> 825 Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, 826 {noreply, NState}), 827 loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1); 828 {ok, {stop, Reason, NState}} -> 829 terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, Debug); 830 {'EXIT', Class, Reason, Stacktrace} -> 831 terminate(Class, Reason, Stacktrace, Name, From, Msg, Mod, State, Debug); 832 {ok, BadReply} -> 833 terminate({bad_return_value, BadReply}, ?STACKTRACE(), Name, From, Msg, Mod, State, Debug) 834 end. 835 836reply(Name, From, Reply, State, Debug) -> 837 reply(From, Reply), 838 sys:handle_debug(Debug, fun print_event/3, Name, 839 {out, Reply, From, State} ). 840 841 842%%----------------------------------------------------------------- 843%% Callback functions for system messages handling. 844%%----------------------------------------------------------------- 845system_continue(Parent, Debug, [Name, State, Mod, Time, HibernateAfterTimeout]) -> 846 loop(Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug). 847 848-spec system_terminate(_, _, _, [_]) -> no_return(). 849 850system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _HibernateAfterTimeout]) -> 851 terminate(Reason, ?STACKTRACE(), Name, undefined, [], Mod, State, Debug). 852 853system_code_change([Name, State, Mod, Time, HibernateAfterTimeout], _Module, OldVsn, Extra) -> 854 case catch Mod:code_change(OldVsn, State, Extra) of 855 {ok, NewState} -> {ok, [Name, NewState, Mod, Time, HibernateAfterTimeout]}; 856 Else -> Else 857 end. 858 859system_get_state([_Name, State, _Mod, _Time, _HibernateAfterTimeout]) -> 860 {ok, State}. 861 862system_replace_state(StateFun, [Name, State, Mod, Time, HibernateAfterTimeout]) -> 863 NState = StateFun(State), 864 {ok, NState, [Name, NState, Mod, Time, HibernateAfterTimeout]}. 865 866%%----------------------------------------------------------------- 867%% Format debug messages. Print them as the call-back module sees 868%% them, not as the real erlang messages. Use trace for that. 869%%----------------------------------------------------------------- 870print_event(Dev, {in, Msg}, Name) -> 871 case Msg of 872 {'$gen_call', {From, _Tag}, Call} -> 873 io:format(Dev, "*DBG* ~tp got call ~tp from ~tw~n", 874 [Name, Call, From]); 875 {'$gen_cast', Cast} -> 876 io:format(Dev, "*DBG* ~tp got cast ~tp~n", 877 [Name, Cast]); 878 _ -> 879 io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg]) 880 end; 881print_event(Dev, {out, Msg, {To,_Tag}, State}, Name) -> 882 io:format(Dev, "*DBG* ~tp sent ~tp to ~tw, new state ~tp~n", 883 [Name, Msg, To, State]); 884print_event(Dev, {noreply, State}, Name) -> 885 io:format(Dev, "*DBG* ~tp new state ~tp~n", [Name, State]); 886print_event(Dev, Event, Name) -> 887 io:format(Dev, "*DBG* ~tp dbg ~tp~n", [Name, Event]). 888 889 890%%% --------------------------------------------------- 891%%% Terminate the server. 892%%% 893%%% terminate/8 is triggered by {stop, Reason} or bad 894%%% return values. The stacktrace is generated via the 895%%% ?STACKTRACE() macro and the ReportReason must not 896%%% be wrapped in tuples. 897%%% 898%%% terminate/9 is triggered in case of error/exit in 899%%% the user callback. In this case the report reason 900%%% always includes the user stacktrace. 901%%% 902%%% The reason received in the terminate/2 callbacks 903%%% always includes the stacktrace for errors and never 904%%% for exits. 905%%% --------------------------------------------------- 906 907-spec terminate(_, _, _, _, _, _, _, _) -> no_return(). 908terminate(Reason, Stacktrace, Name, From, Msg, Mod, State, Debug) -> 909 terminate(exit, Reason, Stacktrace, Reason, Name, From, Msg, Mod, State, Debug). 910 911-spec terminate(_, _, _, _, _, _, _, _, _) -> no_return(). 912terminate(Class, Reason, Stacktrace, Name, From, Msg, Mod, State, Debug) -> 913 ReportReason = {Reason, Stacktrace}, 914 terminate(Class, Reason, Stacktrace, ReportReason, Name, From, Msg, Mod, State, Debug). 915 916-spec terminate(_, _, _, _, _, _, _, _, _, _) -> no_return(). 917terminate(Class, Reason, Stacktrace, ReportReason, Name, From, Msg, Mod, State, Debug) -> 918 Reply = try_terminate(Mod, terminate_reason(Class, Reason, Stacktrace), State), 919 case Reply of 920 {'EXIT', C, R, S} -> 921 error_info({R, S}, Name, From, Msg, Mod, State, Debug), 922 erlang:raise(C, R, S); 923 _ -> 924 case {Class, Reason} of 925 {exit, normal} -> ok; 926 {exit, shutdown} -> ok; 927 {exit, {shutdown,_}} -> ok; 928 _ -> 929 error_info(ReportReason, Name, From, Msg, Mod, State, Debug) 930 end 931 end, 932 case Stacktrace of 933 [] -> 934 erlang:Class(Reason); 935 _ -> 936 erlang:raise(Class, Reason, Stacktrace) 937 end. 938 939terminate_reason(error, Reason, Stacktrace) -> {Reason, Stacktrace}; 940terminate_reason(exit, Reason, _Stacktrace) -> Reason. 941 942error_info(_Reason, application_controller, _From, _Msg, _Mod, _State, _Debug) -> 943 %% OTP-5811 Don't send an error report if it's the system process 944 %% application_controller which is terminating - let init take care 945 %% of it instead 946 ok; 947error_info(Reason, Name, From, Msg, Mod, State, Debug) -> 948 Log = sys:get_log(Debug), 949 ?LOG_ERROR(#{label=>{gen_server,terminate}, 950 name=>Name, 951 last_message=>Msg, 952 state=>format_status(terminate, Mod, get(), State), 953 log=>format_log_state(Mod, Log), 954 reason=>Reason, 955 client_info=>client_stacktrace(From)}, 956 #{domain=>[otp], 957 report_cb=>fun gen_server:format_log/2, 958 error_logger=>#{tag=>error, 959 report_cb=>fun gen_server:format_log/1}}), 960 ok. 961 962client_stacktrace(undefined) -> 963 undefined; 964client_stacktrace({From,_Tag}) -> 965 client_stacktrace(From); 966client_stacktrace(From) when is_pid(From), node(From) =:= node() -> 967 case process_info(From, [current_stacktrace, registered_name]) of 968 undefined -> 969 {From,dead}; 970 [{current_stacktrace, Stacktrace}, {registered_name, []}] -> 971 {From,{From,Stacktrace}}; 972 [{current_stacktrace, Stacktrace}, {registered_name, Name}] -> 973 {From,{Name,Stacktrace}} 974 end; 975client_stacktrace(From) when is_pid(From) -> 976 {From,remote}. 977 978 979%% format_log/1 is the report callback used by Logger handler 980%% error_logger only. It is kept for backwards compatibility with 981%% legacy error_logger event handlers. This function must always 982%% return {Format,Args} compatible with the arguments in this module's 983%% calls to error_logger prior to OTP-21.0. 984format_log(Report) -> 985 Depth = error_logger:get_format_depth(), 986 FormatOpts = #{chars_limit => unlimited, 987 depth => Depth, 988 single_line => false, 989 encoding => utf8}, 990 format_log_multi(limit_report(Report,Depth),FormatOpts). 991 992limit_report(Report,unlimited) -> 993 Report; 994limit_report(#{label:={gen_server,terminate}, 995 last_message:=Msg, 996 state:=State, 997 log:=Log, 998 reason:=Reason, 999 client_info:=Client}=Report, 1000 Depth) -> 1001 Report#{last_message=>io_lib:limit_term(Msg,Depth), 1002 state=>io_lib:limit_term(State,Depth), 1003 log=>[io_lib:limit_term(L,Depth)||L<-Log], 1004 reason=>io_lib:limit_term(Reason,Depth), 1005 client_info=>limit_client_report(Client,Depth)}; 1006limit_report(#{label:={gen_server,no_handle_info}, 1007 message:=Msg}=Report,Depth) -> 1008 Report#{message=>io_lib:limit_term(Msg,Depth)}. 1009 1010limit_client_report({From,{Name,Stacktrace}},Depth) -> 1011 {From,{Name,io_lib:limit_term(Stacktrace,Depth)}}; 1012limit_client_report(Client,_) -> 1013 Client. 1014 1015%% format_log/2 is the report callback for any Logger handler, except 1016%% error_logger. 1017format_log(Report, FormatOpts0) -> 1018 Default = #{chars_limit => unlimited, 1019 depth => unlimited, 1020 single_line => false, 1021 encoding => utf8}, 1022 FormatOpts = maps:merge(Default,FormatOpts0), 1023 IoOpts = 1024 case FormatOpts of 1025 #{chars_limit:=unlimited} -> 1026 []; 1027 #{chars_limit:=Limit} -> 1028 [{chars_limit,Limit}] 1029 end, 1030 {Format,Args} = format_log_single(Report, FormatOpts), 1031 io_lib:format(Format, Args, IoOpts). 1032 1033format_log_single(#{label:={gen_server,terminate}, 1034 name:=Name, 1035 last_message:=Msg, 1036 state:=State, 1037 log:=Log, 1038 reason:=Reason, 1039 client_info:=Client}, 1040 #{single_line:=true,depth:=Depth}=FormatOpts) -> 1041 P = p(FormatOpts), 1042 Format1 = lists:append(["Generic server ",P," terminating. Reason: ",P, 1043 ". Last message: ", P, ". State: ",P,"."]), 1044 {ServerLogFormat,ServerLogArgs} = format_server_log_single(Log,FormatOpts), 1045 {ClientLogFormat,ClientLogArgs} = format_client_log_single(Client,FormatOpts), 1046 1047 Args1 = 1048 case Depth of 1049 unlimited -> 1050 [Name,fix_reason(Reason),Msg,State]; 1051 _ -> 1052 [Name,Depth,fix_reason(Reason),Depth,Msg,Depth,State,Depth] 1053 end, 1054 {Format1++ServerLogFormat++ClientLogFormat, 1055 Args1++ServerLogArgs++ClientLogArgs}; 1056format_log_single(#{label:={gen_server,no_handle_info}, 1057 module:=Mod, 1058 message:=Msg}, 1059 #{single_line:=true,depth:=Depth}=FormatOpts) -> 1060 P = p(FormatOpts), 1061 Format = lists:append(["Undefined handle_info in ",P, 1062 ". Unhandled message: ",P,"."]), 1063 Args = 1064 case Depth of 1065 unlimited -> 1066 [Mod,Msg]; 1067 _ -> 1068 [Mod,Depth,Msg,Depth] 1069 end, 1070 {Format,Args}; 1071format_log_single(Report,FormatOpts) -> 1072 format_log_multi(Report,FormatOpts). 1073 1074format_log_multi(#{label:={gen_server,terminate}, 1075 name:=Name, 1076 last_message:=Msg, 1077 state:=State, 1078 log:=Log, 1079 reason:=Reason, 1080 client_info:=Client}, 1081 #{depth:=Depth}=FormatOpts) -> 1082 Reason1 = fix_reason(Reason), 1083 {ClientFmt,ClientArgs} = format_client_log(Client,FormatOpts), 1084 P = p(FormatOpts), 1085 Format = 1086 lists:append( 1087 ["** Generic server ",P," terminating \n" 1088 "** Last message in was ",P,"~n" 1089 "** When Server state == ",P,"~n" 1090 "** Reason for termination ==~n** ",P,"~n"] ++ 1091 case Log of 1092 [] -> []; 1093 _ -> ["** Log ==~n** ["| 1094 lists:join(",~n ",lists:duplicate(length(Log),P))]++ 1095 ["]~n"] 1096 end) ++ ClientFmt, 1097 Args = 1098 case Depth of 1099 unlimited -> 1100 [Name, Msg, State, Reason1] ++ Log ++ ClientArgs; 1101 _ -> 1102 [Name, Depth, Msg, Depth, State, Depth, Reason1, Depth] ++ 1103 case Log of 1104 [] -> []; 1105 _ -> lists:flatmap(fun(L) -> [L, Depth] end, Log) 1106 end ++ ClientArgs 1107 end, 1108 {Format,Args}; 1109format_log_multi(#{label:={gen_server,no_handle_info}, 1110 module:=Mod, 1111 message:=Msg}, 1112 #{depth:=Depth}=FormatOpts) -> 1113 P = p(FormatOpts), 1114 Format = 1115 "** Undefined handle_info in ~p~n" 1116 "** Unhandled message: "++P++"~n", 1117 Args = 1118 case Depth of 1119 unlimited -> 1120 [Mod,Msg]; 1121 _ -> 1122 [Mod,Msg,Depth] 1123 end, 1124 {Format,Args}. 1125 1126fix_reason({undef,[{M,F,A,L}|MFAs]}=Reason) -> 1127 case code:is_loaded(M) of 1128 false -> 1129 {'module could not be loaded',[{M,F,A,L}|MFAs]}; 1130 _ -> 1131 case erlang:function_exported(M, F, length(A)) of 1132 true -> 1133 Reason; 1134 false -> 1135 {'function not exported',[{M,F,A,L}|MFAs]} 1136 end 1137 end; 1138fix_reason(Reason) -> 1139 Reason. 1140 1141format_server_log_single([],_) -> 1142 {"",[]}; 1143format_server_log_single(Log,FormatOpts) -> 1144 Args = 1145 case maps:get(depth,FormatOpts) of 1146 unlimited -> 1147 [Log]; 1148 Depth -> 1149 [Log, Depth] 1150 end, 1151 {" Log: "++p(FormatOpts),Args}. 1152 1153format_client_log_single(undefined,_) -> 1154 {"",[]}; 1155format_client_log_single({From,dead},_) -> 1156 {" Client ~0p is dead.",[From]}; 1157format_client_log_single({From,remote},_) -> 1158 {" Client ~0p is remote on node ~0p.", [From, node(From)]}; 1159format_client_log_single({_From,{Name,Stacktrace0}},FormatOpts) -> 1160 P = p(FormatOpts), 1161 %% Minimize the stacktrace a bit for single line reports. This is 1162 %% hopefully enough to point out the position. 1163 Stacktrace = lists:sublist(Stacktrace0,4), 1164 Args = 1165 case maps:get(depth,FormatOpts) of 1166 unlimited -> 1167 [Name, Stacktrace]; 1168 Depth -> 1169 [Name, Depth, Stacktrace, Depth] 1170 end, 1171 {" Client "++P++" stacktrace: "++P++".", Args}. 1172 1173format_client_log(undefined,_) -> 1174 {"", []}; 1175format_client_log({From,dead},_) -> 1176 {"** Client ~p is dead~n", [From]}; 1177format_client_log({From,remote},_) -> 1178 {"** Client ~p is remote on node ~p~n", [From, node(From)]}; 1179format_client_log({_From,{Name,Stacktrace}},FormatOpts) -> 1180 P = p(FormatOpts), 1181 Format = lists:append(["** Client ",P," stacktrace~n", 1182 "** ",P,"~n"]), 1183 Args = 1184 case maps:get(depth,FormatOpts) of 1185 unlimited -> 1186 [Name, Stacktrace]; 1187 Depth -> 1188 [Name, Depth, Stacktrace, Depth] 1189 end, 1190 {Format,Args}. 1191 1192p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) -> 1193 "~"++single(Single)++mod(Enc)++p(Depth); 1194p(unlimited) -> 1195 "p"; 1196p(_Depth) -> 1197 "P". 1198 1199single(true) -> "0"; 1200single(false) -> "". 1201 1202mod(latin1) -> ""; 1203mod(_) -> "t". 1204 1205%%----------------------------------------------------------------- 1206%% Status information 1207%%----------------------------------------------------------------- 1208format_status(Opt, StatusData) -> 1209 [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, _HibernateAfterTimeout]] = StatusData, 1210 Header = gen:format_status_header("Status for generic server", Name), 1211 Log = sys:get_log(Debug), 1212 Specific = case format_status(Opt, Mod, PDict, State) of 1213 S when is_list(S) -> S; 1214 S -> [S] 1215 end, 1216 [{header, Header}, 1217 {data, [{"Status", SysState}, 1218 {"Parent", Parent}, 1219 {"Logged events", format_log_state(Mod, Log)}]} | 1220 Specific]. 1221 1222format_log_state(Mod, Log) -> 1223 [case Event of 1224 {out,Msg,From,State} -> 1225 {out,Msg,From,format_status(terminate, Mod, get(), State)}; 1226 {noreply,State} -> 1227 {noreply,format_status(terminate, Mod, get(), State)}; 1228 _ -> Event 1229 end || Event <- Log]. 1230 1231format_status(Opt, Mod, PDict, State) -> 1232 DefStatus = case Opt of 1233 terminate -> State; 1234 _ -> [{data, [{"State", State}]}] 1235 end, 1236 case erlang:function_exported(Mod, format_status, 2) of 1237 true -> 1238 case catch Mod:format_status(Opt, [PDict, State]) of 1239 {'EXIT', _} -> DefStatus; 1240 Else -> Else 1241 end; 1242 _ -> 1243 DefStatus 1244 end. 1245