1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2016. 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 21%% 22%% Information and debug functions. 23%% 24 25-module(diameter_dbg). 26 27-export([table/1, 28 tables/0, 29 fields/1, 30 modules/0, 31 versions/0, 32 version_info/0, 33 compiled/0, 34 procs/0, 35 latest/0, 36 nl/0, 37 sizes/0]). 38 39-export([diameter_config/0, 40 diameter_peer/0, 41 diameter_reg/0, 42 diameter_request/0, 43 diameter_sequence/0, 44 diameter_service/0, 45 diameter_stats/0]). 46 47-export([pp/1, 48 subscriptions/0, 49 children/0]). 50 51%% Trace help. 52-export([tracer/0, tracer/1, 53 p/0, p/1, 54 stop/0, 55 tpl/1, 56 tp/1]). 57 58-include_lib("diameter/include/diameter.hrl"). 59 60-define(APP, diameter). 61-define(I, diameter_info). 62 63-define(LOCAL, [diameter_config, 64 diameter_peer, 65 diameter_reg, 66 diameter_request, 67 diameter_sequence, 68 diameter_service, 69 diameter_stats]). 70 71-define(VALUES(Rec), tl(tuple_to_list(Rec))). 72 73%% ---------------------------------------------------------- 74%% # sizes/0 75%% 76%% Return sizes of named tables. 77%% ---------------------------------------------------------- 78 79sizes() -> 80 [{T, ets:info(T, size)} || T <- ?LOCAL, T /= diameter_peer]. 81 82%% ---------------------------------------------------------- 83%% # table/1 84%% 85%% Pretty-print a diameter table. Returns the number of records 86%% printed, or undefined. 87%% ---------------------------------------------------------- 88 89table(T) 90 when (T == diameter_peer) orelse (T == diameter_reg) -> 91 ?I:format(collect(T), fields(T), fun ?I:split/2); 92 93table(Table) 94 when is_atom(Table) -> 95 case fields(Table) of 96 undefined = No -> 97 No; 98 Fields -> 99 ?I:format(Table, Fields, fun split/2) 100 end. 101 102split([started, name | Fs], [S, N | Vs]) -> 103 {name, [started | Fs], N, [S | Vs]}; 104split([[F|FT]|Fs], [Rec|Vs]) -> 105 [_, V | VT] = tuple_to_list(Rec), 106 {F, FT ++ Fs, V, VT ++ Vs}; 107split([F|Fs], [V|Vs]) -> 108 {F, Fs, V, Vs}. 109 110%% ---------------------------------------------------------- 111%% # TableName/0 112%% ---------------------------------------------------------- 113 114-define(TABLE(Name), Name() -> table(Name)). 115 116?TABLE(diameter_config). 117?TABLE(diameter_peer). 118?TABLE(diameter_reg). 119?TABLE(diameter_request). 120?TABLE(diameter_sequence). 121?TABLE(diameter_service). 122?TABLE(diameter_stats). 123 124%% ---------------------------------------------------------- 125%% # tables/0 126%% 127%% Pretty-print diameter tables from all nodes. Returns the number of 128%% records printed. 129%% ---------------------------------------------------------- 130 131tables() -> 132 ?I:format(field(?LOCAL), fun split/3, fun collect/1). 133 134field(Tables) -> 135 lists:map(fun(T) -> {T, fields(T)} end, lists:sort(Tables)). 136 137split(_, Fs, Vs) -> 138 split(Fs, Vs). 139 140%% ---------------------------------------------------------- 141%% # modules/0 142%% ---------------------------------------------------------- 143 144modules() -> 145 Path = filename:join([appdir(), atom_to_list(?APP) ++ ".app"]), 146 {ok, [{application, ?APP, Attrs}]} = file:consult(Path), 147 {modules, Mods} = lists:keyfind(modules, 1, Attrs), 148 Mods. 149 150appdir() -> 151 [_|_] = code:lib_dir(?APP, ebin). 152 153%% ---------------------------------------------------------- 154%% # versions/0 155%% ---------------------------------------------------------- 156 157versions() -> 158 ?I:versions(modules()). 159 160%% ---------------------------------------------------------- 161%% # version_info/0 162%% ---------------------------------------------------------- 163 164version_info() -> 165 ?I:version_info(modules()). 166 167%% ---------------------------------------------------------- 168%% # compiled/0 169%% ---------------------------------------------------------- 170 171compiled() -> 172 ?I:compiled(modules()). 173 174%% ---------------------------------------------------------- 175%% # procs/0 176%% ---------------------------------------------------------- 177 178procs() -> 179 ?I:procs(?APP). 180 181%% ---------------------------------------------------------- 182%% # latest/0 183%% ---------------------------------------------------------- 184 185latest() -> 186 ?I:latest(modules()). 187 188%% ---------------------------------------------------------- 189%% # nl/0 190%% ---------------------------------------------------------- 191 192nl() -> 193 lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()). 194 195%% ---------------------------------------------------------- 196%% # pp/1 197%% 198%% Description: Pretty-print a message binary. 199%% ---------------------------------------------------------- 200 201%% Network byte order = big endian. 202 203pp(<<Version:8, MsgLength:24, 204 Rbit:1, Pbit:1, Ebit:1, Tbit:1, Reserved:4, CmdCode:24, 205 ApplId:32, 206 HbHid:32, 207 E2Eid:32, 208 AVPs/binary>>) -> 209 ?I:sep(), 210 ppp(["Version", 211 "Message length", 212 "[Actual length]", 213 "R(equest)", 214 "P(roxiable)", 215 "E(rror)", 216 "T(Potential retrans)", 217 "Reserved bits", 218 "Command code", 219 "Application id", 220 "Hop by hop id", 221 "End to end id"], 222 [Version, MsgLength, size(AVPs) + 20, 223 Rbit, Pbit, Ebit, Tbit, Reserved, 224 CmdCode, 225 ApplId, 226 HbHid, 227 E2Eid]), 228 N = avp_loop({AVPs, MsgLength - 20}, 0), 229 ?I:sep(), 230 N; 231 232pp(<<_Version:8, MsgLength:24, _/binary>> = Bin) -> 233 {bad_message_length, MsgLength, size(Bin)}; 234 235pp(Bin) 236 when is_binary(Bin) -> 237 {truncated_binary, size(Bin)}; 238 239pp(_) -> 240 not_binary. 241 242%% avp_loop/2 243 244avp_loop({Bin, Size}, N) -> 245 avp_loop(avp(Bin, Size), N+1); 246avp_loop(ok, N) -> 247 N; 248avp_loop([_E, _Rest] = L, N) -> 249 io:format("! ~s: ~p~n", L), 250 N; 251avp_loop([E, Rest, Fmt | Values], N) 252 when is_binary(Rest) -> 253 io:format("! ~s (" ++ Fmt ++ "): ~p~n", [E|Values] ++ [Rest]), 254 N. 255 256%% avp/2 257 258avp(<<>>, 0) -> 259 ok; 260avp(<<Code:32, Flags:1/binary, Length:24, Rest/binary>>, 261 Size) -> 262 avp(Code, Flags, Length, Rest, Size); 263avp(Bin, _) -> 264 ["truncated AVP header", Bin]. 265 266%% avp/5 267 268avp(Code, Flags, Length, Rest, Size) -> 269 <<V:1, M:1, P:1, Res:5>> 270 = Flags, 271 b(), 272 ppp(["AVP Code", 273 "V(endor)", 274 "M(andatory)", 275 "P(Security)", 276 "R(eserved)", 277 "Length"], 278 [Code, V, M, P, Res, Length]), 279 avp(V, Rest, Length - 8, Size - 8). 280 281%% avp/4 282 283avp(1, <<V:32, Data/binary>>, Length, Size) -> 284 ppp({"Vendor-ID", V}), 285 data(Data, Length - 4, Size - 4); 286avp(1, Bin, _, _) -> 287 ["truncated Vendor-ID", Bin]; 288avp(0, Data, Length, Size) -> 289 data(Data, Length, Size). 290 291data(Bin, Length, Size) 292 when size(Bin) >= Length -> 293 <<AVP:Length/binary, Rest/binary>> = Bin, 294 ppp({"Data", AVP}), 295 unpad(Rest, Size - Length, Length rem 4); 296 297data(Bin, _, _) -> 298 ["truncated AVP data", Bin]. 299 300%% Remove padding bytes up to the next word boundary. 301unpad(Bin, Size, 0) -> 302 {Bin, Size}; 303unpad(Bin, Size, N) -> 304 un(Bin, Size, 4 - N). 305 306un(Bin, Size, N) 307 when size(Bin) >= N -> 308 ppp({"Padding bytes", N}), 309 <<Pad:N/binary, Rest/binary>> = Bin, 310 Bits = N*8, 311 case Pad of 312 <<0:Bits>> -> 313 {Rest, Size - N}; 314 _ -> 315 ["non-zero padding", Bin, "~p", N] 316 end; 317 318un(Bin, _, _) -> 319 ["truncated padding", Bin]. 320 321b() -> 322 io:format("#~n"). 323 324ppp(Fields, Values) -> 325 lists:foreach(fun ppp/1, lists:zip(Fields, Values)). 326 327ppp({Field, Value}) -> 328 io:format(": ~-22s : ~p~n", [Field, Value]). 329 330%% ---------------------------------------------------------- 331%% # subscriptions/0 332%% 333%% Returns a list of {SvcName, Pid}. 334%% ---------------------------------------------------------- 335 336subscriptions() -> 337 diameter_service:subscriptions(). 338 339%% ---------------------------------------------------------- 340%% # children/0 341%% ---------------------------------------------------------- 342 343children() -> 344 diameter_sup:tree(). 345 346%% ---------------------------------------------------------- 347 348%% tracer/[12] 349 350tracer(Port) 351 when is_integer(Port) -> 352 dbg:tracer(port, dbg:trace_port(ip, Port)); 353 354tracer(Path) 355 when is_list(Path) -> 356 dbg:tracer(port, dbg:trace_port(file, Path)). 357 358tracer() -> 359 dbg:tracer(process, {fun p/2, ok}). 360 361p(T,_) -> 362 io:format("+ ~p~n", [T]). 363 364%% p/[01] 365 366p() -> 367 p([c,timestamp]). 368 369p(T) -> 370 dbg:p(all,T). 371 372%% stop/0 373 374stop() -> 375 dbg:ctp(), 376 dbg:stop_clear(). 377 378%% tpl/1 379%% tp/1 380 381tpl(T) -> 382 dbg(tpl, T). 383 384tp(T) -> 385 dbg(tp, T). 386 387%% dbg/2 388 389dbg(F, L) 390 when is_list(L) -> 391 [dbg(F, X) || X <- L]; 392 393dbg(F, M) 394 when is_atom(M) -> 395 apply(dbg, F, [M, x]); 396 397dbg(F, T) 398 when is_tuple(T) -> 399 apply(dbg, F, tuple_to_list(T)). 400 401%% =========================================================================== 402%% =========================================================================== 403 404%% collect/1 405 406collect(diameter_peer) -> 407 lists:flatmap(fun peers/1, diameter:services()); 408 409collect(diameter_reg) -> 410 diameter_reg:terms(); 411 412collect(Name) -> 413 c(ets:info(Name), Name). 414 415c(undefined, _) -> 416 []; 417c(_, Name) -> 418 ets:tab2list(Name). 419 420%% peers/1 421 422peers(Name) -> 423 peers(Name, diameter:service_info(Name, transport)). 424 425peers(_, undefined) -> 426 []; 427peers(Name, Ts) -> 428 lists:flatmap(fun(T) -> mk_peers(Name, T) end, Ts). 429 430mk_peers(Name, [_, {type, connect} | _] = Ts) -> 431 [[Name | mk_peer(Ts)]]; 432mk_peers(Name, [R, {type, listen}, O, {accept = A, As} | _]) -> 433 [[Name | mk_peer([R, {type, A}, O | Ts])] || Ts <- As]. 434%% This is a bit lame: service_info works to build this list and out 435%% of something like what we want here and then we take it apart. 436 437mk_peer(Vs) -> 438 [Type, Ref, State, Opts, WPid, TPid, SApps, Caps] 439 = get_values(Vs, [type,ref,state,options,watchdog,peer,apps,caps]), 440 [Ref, State, [{type, Type} | Opts], s(WPid), s(TPid), SApps, Caps]. 441 442get_values(Vs, Ks) -> 443 [proplists:get_value(K, Vs) || K <- Ks]. 444 445s(undefined = T) -> 446 T; 447s({Pid, _Started, _State}) -> 448 state(Pid); 449s({Pid, _Started}) -> 450 state(Pid). 451 452%% Collect states from watchdog/transport pids. 453state(Pid) -> 454 MRef = erlang:monitor(process, Pid), 455 Pid ! {state, self()}, 456 receive 457 {'DOWN', MRef, process, _, _} -> 458 Pid; 459 {Pid, _} = T -> 460 erlang:demonitor(MRef, [flush]), 461 T 462 end. 463 464%% fields/1 465 466-define(FIELDS(Table), fields(Table) -> record_info(fields, Table)). 467 468fields(diameter_config) -> 469 []; 470 471fields(T) 472 when T == diameter_request; 473 T == diameter_sequence -> 474 fun kv/1; 475 476fields(diameter_stats) -> 477 fun({Ctr, N}) when not is_pid(Ctr) -> 478 {[counter, value], [Ctr, N]}; 479 (_) -> 480 [] 481 end; 482 483fields(diameter_service) -> 484 [started, 485 name, 486 record_info(fields, diameter_service), 487 watchdogT, 488 peerT, 489 shared_peers, 490 local_peers, 491 monitor, 492 options]; 493 494?FIELDS(diameter_event); 495?FIELDS(diameter_uri); 496?FIELDS(diameter_avp); 497?FIELDS(diameter_header); 498?FIELDS(diameter_packet); 499?FIELDS(diameter_app); 500?FIELDS(diameter_caps); 501 502fields(diameter_peer) -> 503 [service, ref, state, options, watchdog, peer, applications, capabilities]; 504 505fields(diameter_reg) -> 506 [property, pids]; 507 508fields(_) -> 509 undefined. 510 511kv({_,_}) -> 512 [key, value]; 513kv(_) -> 514 []. 515