1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2005-2011. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: 21: %%%------------------------------------------------------------------- 22: %%% File : system_info_SUITE.erl 23: %%% Author : Rickard Green <rickard.s.green@ericsson.com> 24: %%% Description : Misc tests of erlang:system_info/1 25: %%% 26: %%% Created : 15 Jul 2005 by Rickard Green <rickard.s.green@ericsson.com> 27: %%%------------------------------------------------------------------- 28: -module(system_info_SUITE). 29: -author('rickard.s.green@ericsson.com'). 30: 31: %-define(line_trace, 1). 32: 33: -include_lib("test_server/include/test_server.hrl"). 34: 35: %-compile(export_all). 36: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 37: init_per_group/2,end_per_group/2, 38: init_per_testcase/2, end_per_testcase/2]). 39: 40: -export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, 41: ets_limit/1]). 42: 43: -define(DEFAULT_TIMEOUT, ?t:minutes(2)). 44: 45: suite() -> [{ct_hooks,[ts_install_cth]}]. 46: 47: all() -> 48: [process_count, system_version, misc_smoke_tests, 49: heap_size, wordsize, memory, ets_limit]. 50: 51: groups() -> 52: []. 53: 54: init_per_suite(Config) -> 55: Config. 56: 57: end_per_suite(_Config) -> 58: ok. 59: 60: init_per_group(_GroupName, Config) -> 61: Config. 62: 63: end_per_group(_GroupName, Config) -> 64: Config. 65: 66: 67: init_per_testcase(_Case, Config) when is_list(Config) -> 68: Dog = ?t:timetrap(?DEFAULT_TIMEOUT), 69: [{watchdog, Dog}|Config]. 70: 71: end_per_testcase(_Case, Config) when is_list(Config) -> 72: Dog = ?config(watchdog, Config), 73: ?t:timetrap_cancel(Dog), 74: ok. 75: 76: %%% 77: %%% The test cases ------------------------------------------------------------- 78: %%% 79: 80: process_count(doc) -> []; 81: process_count(suite) -> []; 82: process_count(Config) when is_list(Config) -> 83: case catch erlang:system_info(modified_timing_level) of 84: Level when is_integer(Level) -> 85: {skipped, 86: "Modified timing (level " ++ integer_to_list(Level) 87: ++ ") is enabled. spawn() is too slow for this " 88: " test when modified timing is enabled."}; 89: _ -> 90: process_count_test() 91: end. 92: 93: process_count_test() -> 94: ?line OldPrio = process_flag(priority, max), 95: ?line check_procs(10), 96: ?line check_procs(11234), 97: ?line check_procs(57), 98: ?line check_procs(1030), 99: ?line check_procs(687), 100: ?line check_procs(7923), 101: ?line check_procs(5302), 102: ?line check_procs(12456), 103: ?line check_procs(14), 104: ?line check_procs(1125), 105: ?line check_procs(236), 106: ?line check_procs(125), 107: ?line check_procs(2346), 108: ?line process_flag(priority, OldPrio), 109: ?line ok. 110: 111: 112: check_procs(N) -> 113: ?line CP = length(processes()), 114: ?line Procs = start_procs(N), 115: ?line check_pc(CP+N), 116: ?line stop_procs(Procs), 117: ?line check_pc(CP). 118: 119: check_pc(E) -> 120: ?line P = length(processes()), 121: ?line SI = erlang:system_info(process_count), 122: ?line ?t:format("E=~p; P=~p; SI=~p~n", [E, P, SI]), 123: ?line E = P, 124: ?line P = SI. 125: 126: start_procs(N) -> 127: lists:map(fun (_) -> 128: P = spawn_opt(fun () -> 129: receive after infinity -> bye end 130: end, 131: [{priority, max}]), 132: {P, erlang:monitor(process, P)} 133: end, 134: lists:seq(1, N)). 135: 136: stop_procs(PMs) -> 137: lists:foreach(fun ({P, _}) -> 138: exit(P, boom) 139: end, PMs), 140: lists:foreach(fun ({P, M}) -> 141: receive {'DOWN', M, process, P, boom} -> ok end 142: end, PMs). 143: 144: 145: system_version(doc) -> []; 146: system_version(suite) -> []; 147: system_version(Config) when is_list(Config) -> 148: ?line {comment, erlang:system_info(system_version)}. 149: 150: misc_smoke_tests(doc) -> []; 151: misc_smoke_tests(suite) -> []; 152: misc_smoke_tests(Config) when is_list(Config) -> 153: ?line true = is_binary(erlang:system_info(info)), 154: ?line true = is_binary(erlang:system_info(procs)), 155: ?line true = is_binary(erlang:system_info(loaded)), 156: ?line true = is_binary(erlang:system_info(dist)), 157: ?line ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end, 158: ?line ok. 159: 160: 161: heap_size(doc) -> []; 162: heap_size(suite) -> []; 163: heap_size(Config) when is_list(Config) -> 164: ?line {min_bin_vheap_size, VHmin} = erlang:system_info(min_bin_vheap_size), 165: ?line {min_heap_size, Hmin} = erlang:system_info(min_heap_size), 166: ?line GCinf = erlang:system_info(garbage_collection), 167: ?line VHmin = proplists:get_value(min_bin_vheap_size, GCinf), 168: ?line Hmin = proplists:get_value(min_heap_size, GCinf), 169: ok. 170: 171: wordsize(suite) -> 172: []; 173: wordsize(doc) -> 174: ["Tests the various wordsize variants"]; 175: wordsize(Config) when is_list(Config) -> 176: ?line A = erlang:system_info(wordsize), 177: ?line true = is_integer(A), 178: ?line A = erlang:system_info({wordsize,internal}), 179: ?line B = erlang:system_info({wordsize,external}), 180: ?line true = A =< B, 181: case {B,A} of 182: {4,4} -> 183: {comment, "True 32-bit emulator"}; 184: {8,8} -> 185: {comment, "True 64-bit emulator"}; 186: {8,4} -> 187: {comment, "Halfword 64-bit emulator"}; 188: Other -> 189: exit({unexpected_wordsizes,Other}) 190: end. 191: 192: memory(doc) -> ["Verify that erlang:memory/0 and memory results in crashdump produce are similar"]; 193: memory(Config) when is_list(Config) -> 194: %% 195: %% Verify that erlang:memory/0 and memory results in 196: %% crashdump produce are similar. 197: %% 198: %% erlang:memory/0 requests information from each scheduler 199: %% thread and puts the information together in erlang code 200: %% (erlang.erl). 201: %% 202: %% When a crash dump is written we cannot use the 203: %% erlang:memory/0 implementation. The crashdump implementation 204: %% is a pure C implementation inspecting all allocator instances 205: %% after the system has been blocked (erts_memory() in erl_alloc.c). 206: %% 207: %% Since we got two implementations, modifications can easily 208: %% cause them to produce different results. 209: %% 210: %% erts_debug:get_internal_state(memory) blocks the system and 211: %% execute the same code as the crash dump writing uses. 212: %% 213: 214: erts_debug:set_internal_state(available_internal_state, true), 215: %% Use a large heap size on the controling process in 216: %% order to avoid changes in its heap size during 217: %% comparisons. 218: MinHeapSize = process_flag(min_heap_size, 1024*1024), 219: Prio = process_flag(priority, max), 220: try 221: erlang:memory(), %% first call will init stat atoms 222: garbage_collect(), %% blow up heap 223: memory_test(Config) 224: catch 225: error:notsup -> {skipped, "erlang:memory() not supported"} 226: after 227: process_flag(min_heap_size, MinHeapSize), 228: process_flag(priority, Prio), 229: catch erts_debug:set_internal_state(available_internal_state, false) 230: end. 231: 232: memory_test(_Config) -> 233: 234: MWs = spawn_mem_workers(), 235: 236: DPs = mem_workers_call(MWs, 237: fun () -> 238: mapn(fun (_) -> 239: spawn(fun () -> 240: receive 241: after infinity -> 242: ok 243: end 244: end) 245: end, 246: 1000 div erlang:system_info(schedulers_online)) 247: end, 248: []), 249: cmp_memory(MWs, "spawn procs"), 250: 251: Ps = lists:flatten(DPs), 252: 253: mem_workers_call(MWs, 254: fun () -> 255: lists:foreach(fun (P) -> link(P) end, Ps) 256: end, 257: []), 258: cmp_memory(MWs, "link procs"), 259: mem_workers_call(MWs, 260: fun () -> 261: lists:foreach(fun (P) -> unlink(P) end, Ps) 262: end, 263: []), 264: cmp_memory(MWs, "unlink procs"), 265: 266: DMs = mem_workers_call(MWs, 267: fun () -> 268: lists:map(fun (P) -> 269: monitor(process, P) 270: end, Ps) 271: end, 272: []), 273: cmp_memory(MWs, "monitor procs"), 274: Ms = lists:flatten(DMs), 275: mem_workers_call(MWs, 276: fun () -> 277: lists:foreach(fun (M) -> 278: demonitor(M) 279: end, Ms) 280: end, 281: []), 282: cmp_memory(MWs, "demonitor procs"), 283: 284: mem_workers_call(MWs, 285: fun () -> 286: lists:foreach(fun (P) -> 287: P ! {a, "message", make_ref()} 288: end, Ps) 289: end, 290: []), 291: cmp_memory(MWs, "message procs"), 292: 293: mem_workers_call(MWs, 294: fun () -> 295: Mons = lists:map(fun (P) -> 296: exit(P, kill), 297: monitor(process, P) 298: end, 299: Ps), 300: lists:foreach(fun (Mon) -> 301: receive 302: {'DOWN', Mon, _, _, _} -> ok 303: end 304: end, 305: Mons) 306: end, []), 307: cmp_memory(MWs, "kill procs"), 308: 309: mem_workers_call(MWs, 310: fun () -> 311: put(binary_data, 312: mapn(fun (_) -> list_to_binary(lists:duplicate(256,$?)) end, 100)) 313: end, 314: []), 315: 316: cmp_memory(MWs, "store binary data"), 317: 318: mem_workers_call(MWs, 319: fun () -> 320: put(binary_data, false), 321: garbage_collect() 322: end, 323: []), 324: cmp_memory(MWs, "release binary data"), 325: 326: mem_workers_call(MWs, 327: fun () -> 328: list_to_atom("an ugly atom "++integer_to_list(erlang:system_info(scheduler_id))), 329: list_to_atom("another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))), 330: list_to_atom("yet another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))) 331: end, 332: []), 333: cmp_memory(MWs, "new atoms"), 334: 335: 336: mem_workers_call(MWs, 337: fun () -> 338: T = ets:new(?MODULE, []), 339: ets:insert(T, {gurka, lists:seq(1,10000)}), 340: ets:insert(T, {banan, lists:seq(1,1024)}), 341: ets:insert(T, {appelsin, make_ref()}), 342: put(ets_id, T) 343: end, 344: []), 345: cmp_memory(MWs, "store ets data"), 346: 347: mem_workers_call(MWs, 348: fun () -> 349: ets:delete(get(ets_id)), 350: put(ets_id, false) 351: end, 352: []), 353: cmp_memory(MWs, "remove ets data"), 354: 355: lists:foreach(fun (MW) -> 356: unlink(MW), 357: Mon = monitor(process, MW), 358: exit(MW, kill), 359: receive 360: {'DOWN', Mon, _, _, _} -> ok 361: end 362: end, 363: MWs), 364: ok. 365: 366: mem_worker() -> 367: receive 368: {call, From, Fun, Args} -> 369: From ! {reply, self(), apply(Fun, Args)}, 370: mem_worker(); 371: {cast, _From, Fun, Args} -> 372: apply(Fun, Args), 373: mem_worker() 374: end. 375: 376: mem_workers_call(MWs, Fun, Args) -> 377: lists:foreach(fun (MW) -> 378: MW ! {call, self(), Fun, Args} 379: end, 380: MWs), 381: lists:map(fun (MW) -> 382: receive 383: {reply, MW, Res} -> 384: Res 385: end 386: end, 387: MWs). 388: 389: mem_workers_cast(MWs, Fun, Args) -> 390: lists:foreach(fun (MW) -> 391: MW ! {cast, self(), Fun, Args} 392: end, 393: MWs). 394: 395: spawn_mem_workers() -> 396: spawn_mem_workers(erlang:system_info(schedulers_online)). 397: 398: spawn_mem_workers(0) -> 399: []; 400: spawn_mem_workers(N) -> 401: [spawn_opt(fun () -> mem_worker() end, 402: [{scheduler, N rem erlang:system_info(schedulers_online) + 1}, 403: link]) | spawn_mem_workers(N-1)]. 404: 405: 406: 407: mem_get(X, Mem) -> 408: case lists:keyfind(X, 1, Mem) of 409: {X, Val} -> Val; 410: false -> false 411: end. 412: 413: cmp_memory(What, Mem1, Mem2, 1) -> 414: R1 = mem_get(What, Mem1), 415: R2 = mem_get(What, Mem2), 416: true = R1 == R2; 417: cmp_memory(What, Mem1, Mem2, RelDiff) -> 418: %% We allow RealDiff diff 419: R1 = mem_get(What, Mem1), 420: R2 = mem_get(What, Mem2), 421: case R1 == R2 of 422: true -> 423: ok; 424: false -> 425: case R1 > R2 of 426: true -> 427: true = R2*RelDiff > R1; 428: false -> 429: true = R1*RelDiff > R2 430: end 431: end. 432: 433: pos_int(Val) when Val >= 0 -> 434: Val; 435: pos_int(Val) -> 436: exit({not_pos_int, Val}). 437: 438: check_sane_memory(Mem) -> 439: Tot = pos_int(mem_get(total, Mem)), 440: Proc = pos_int(mem_get(processes, Mem)), 441: ProcUsed = pos_int(mem_get(processes_used, Mem)), 442: Sys = pos_int(mem_get(system, Mem)), 443: Atom = pos_int(mem_get(atom, Mem)), 444: AtomUsed = pos_int(mem_get(atom_used, Mem)), 445: Bin = pos_int(mem_get(binary, Mem)), 446: Code = pos_int(mem_get(code, Mem)), 447: Ets = pos_int(mem_get(ets, Mem)), 448: 449: Tot = Proc + Sys, 450: true = Sys > Atom + Bin + Code + Ets, 451: true = Proc >= ProcUsed, 452: true = Atom >= AtomUsed, 453: 454: case mem_get(maximum, Mem) of 455: false -> ok; 456: Max -> true = pos_int(Max) >= Tot 457: end, 458: ok. 459: 460: cmp_memory(MWs, Str) -> 461: erlang:display(Str), 462: lists:foreach(fun (MW) -> garbage_collect(MW) end, MWs), 463: garbage_collect(), 464: erts_debug:set_internal_state(wait, deallocations), 465: 466: EDM = erts_debug:get_internal_state(memory), 467: EM = erlang:memory(), 468: 469: io:format("~s:~n" 470: "erlang:memory() = ~p~n" 471: "crash dump memory = ~p~n", 472: [Str, EM, EDM]), 473: 474: ?line check_sane_memory(EM), 475: ?line check_sane_memory(EDM), 476: 477: %% We expect these to always give us exactly the same result 478: 479: ?line cmp_memory(atom, EM, EDM, 1), 480: ?line cmp_memory(atom_used, EM, EDM, 1), 481: ?line cmp_memory(binary, EM, EDM, 1), 482: ?line cmp_memory(code, EM, EDM, 1), 483: ?line cmp_memory(ets, EM, EDM, 1), 484: 485: %% Total, processes, processes_used, and system will seldom 486: %% give us exactly the same result since the two readings 487: %% aren't taken atomically. 488: 489: ?line cmp_memory(total, EM, EDM, 1.05), 490: ?line cmp_memory(processes, EM, EDM, 1.05), 491: ?line cmp_memory(processes_used, EM, EDM, 1.05), 492: ?line cmp_memory(system, EM, EDM, 1.05), 493: 494: ok. 495: 496: mapn(_Fun, 0) -> 497: []; 498: mapn(Fun, N) -> 499: [Fun(N) | mapn(Fun, N-1)]. 500: 501: ets_limit(doc) -> 502: "Verify system_info(ets_limit) reflects max ETS table settings."; 503: ets_limit(suite) -> []; 504: ets_limit(Config0) when is_list(Config0) -> 505: Config = [{testcase,ets_limit}|Config0], 506: true = is_integer(get_ets_limit(Config)), 507: 12345 = get_ets_limit(Config, 12345), 508: ok. 509: 510: get_ets_limit(Config) -> 511: get_ets_limit(Config, 0). 512: get_ets_limit(Config, EtsMax) -> 513: Envs = case EtsMax of 514: 0 -> []; 515: _ -> [{"ERL_MAX_ETS_TABLES", integer_to_list(EtsMax)}] 516: end, 517: {ok, Node} = start_node(Config, Envs), 518: Me = self(), 519: Ref = make_ref(), 520: spawn_link(Node, 521: fun() -> 522: Res = erlang:system_info(ets_limit), 523: unlink(Me), 524: Me ! {Ref, Res} 525: end), 526: receive 527: {Ref, Res} -> 528: Res 529: end, 530: stop_node(Node), 531: Res. 532: 533: start_node(Config, Envs) when is_list(Config) -> 534: Pa = filename:dirname(code:which(?MODULE)), 535: {A, B, C} = now(), 536: Name = list_to_atom(atom_to_list(?MODULE) 537: ++ "-" 538: ++ atom_to_list(?config(testcase, Config)) 539: ++ "-" 540: ++ integer_to_list(A) 541: ++ "-" 542: ++ integer_to_list(B) 543: ++ "-" 544: ++ integer_to_list(C)), 545: ?t:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). 546: 547: stop_node(Node) -> 548: ?t:stop_node(Node).