1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2003-2012. 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: -module(hibernate_SUITE). 21: 22: -include_lib("test_server/include/test_server.hrl"). 23: 24: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 25: init_per_group/2,end_per_group/2, 26: init_per_testcase/2,end_per_testcase/2, 27: basic/1,dynamic_call/1,min_heap_size/1,bad_args/1, 28: messages_in_queue/1,undefined_mfa/1,no_heap/1,wake_up_and_bif_trap/1]). 29: 30: %% Used by test cases. 31: -export([basic_hibernator/1,dynamic_call_hibernator/2,messages_in_queue_restart/2, no_heap_loop/0,characters_to_list_trap/1]). 32: 33: suite() -> [{ct_hooks,[ts_install_cth]}]. 34: 35: all() -> 36: [basic, dynamic_call, min_heap_size, bad_args, messages_in_queue, 37: undefined_mfa, no_heap, wake_up_and_bif_trap]. 38: 39: groups() -> 40: []. 41: 42: init_per_suite(Config) -> 43: Config. 44: 45: end_per_suite(_Config) -> 46: ok. 47: 48: init_per_group(_GroupName, Config) -> 49: Config. 50: 51: end_per_group(_GroupName, Config) -> 52: Config. 53: 54: 55: init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 56: Dog = ?t:timetrap(?t:minutes(3)), 57: [{watchdog,Dog}|Config]. 58: 59: end_per_testcase(_Func, Config) -> 60: Dog=?config(watchdog, Config), 61: ?t:timetrap_cancel(Dog). 62: 63: %%% 64: %%% Testing the basic functionality of erlang:hibernate/3. 65: %%% 66: 67: basic(Config) when is_list(Config) -> 68: Ref = make_ref(), 69: Info = {self(),Ref}, 70: ExpectedHeapSz = erts_debug:size([Info]), 71: ?line Child = spawn_link(fun() -> basic_hibernator(Info) end), 72: ?line hibernate_wake_up(100, ExpectedHeapSz, Child), 73: ?line Child ! please_quit_now, 74: ok. 75: 76: hibernate_wake_up(0, _, _) -> ok; 77: hibernate_wake_up(N, ExpectedHeapSz, Child) -> 78: {heap_size,Before} = process_info(Child, heap_size), 79: case N rem 2 of 80: 0 -> 81: Child ! {acquire_old_heap,self()}, 82: receive 83: done -> ok 84: end; 85: 1 -> ok 86: end, 87: ?line Child ! {hibernate,self()}, 88: ?line wait_until(fun () -> 89: {current_function,{erlang,hibernate,3}} == 90: process_info(Child, current_function) 91: end), 92: ?line {message_queue_len,0} = process_info(Child, message_queue_len), 93: ?line {status,waiting} = process_info(Child, status), 94: ?line {heap_size,ExpectedHeapSz} = process_info(Child, heap_size), 95: io:format("Before hibernation: ~p After hibernation: ~p\n", 96: [Before,ExpectedHeapSz]), 97: ?line Child ! {whats_up,self()}, 98: ?line receive 99: {all_fine,X,Child,_Ref} -> 100: if 101: N =:= 1 -> io:format("~p\n", [X]); 102: true -> ok 103: end, 104: {backtrace,Bin} = process_info(Child, backtrace), 105: if 106: size(Bin) > 1000 -> 107: io:format("~s\n", [binary_to_list(Bin)]), 108: ?line ?t:fail(stack_is_growing); 109: true -> 110: hibernate_wake_up(N-1, ExpectedHeapSz, Child) 111: end; 112: Other -> 113: ?line io:format("~p\n", [Other]), 114: ?line ?t:fail(unexpected_message) 115: end. 116: 117: basic_hibernator(Info) -> 118: {catchlevel,0} = process_info(self(), catchlevel), 119: receive 120: Any -> 121: basic_hibernator_msg(Any, Info), 122: basic_hibernator(Info) 123: end. 124: 125: basic_hibernator_msg({hibernate,_}, Info) -> 126: catch erlang:hibernate(?MODULE, basic_hibernator, [Info]), 127: exit(hibernate_returned); 128: basic_hibernator_msg({acquire_old_heap,Parent}, _) -> 129: acquire_old_heap(), 130: Parent ! done; 131: basic_hibernator_msg({whats_up,Parent}, {Parent,Ref}) -> 132: {heap_size,HeapSize} = process_info(self(), heap_size), 133: io:format("Heap size after waking up: ~p\n", [HeapSize]), 134: X = whats_up_calc(5000, 2, math:pi(), 4, 5, 6, 7, 8.5, 9, []), 135: Parent ! {all_fine,X,self(),Ref}; 136: basic_hibernator_msg(please_quit_now, _) -> 137: exit(normal); 138: basic_hibernator_msg(Other, _) -> 139: exit({unexpected,Other}). 140: 141: acquire_old_heap() -> 142: case process_info(self(), [heap_size,total_heap_size]) of 143: [{heap_size,Sz},{total_heap_size,Total}] when Sz < Total -> 144: ok; 145: _ -> 146: acquire_old_heap() 147: end. 148: 149: %% The point with this calculation is to force memory to be 150: %% allocated for the argument registers in the process structure. 151: %% The allocation will be forced if the process is scheduled out 152: %% while calling a function with more than 6 arguments. 153: whats_up_calc(0, A2, A3, A4, A5, A6, A7, A8, A9, Acc) -> 154: {Acc,A2+A3+A4+A5+A6+A7+A8+A9}; 155: whats_up_calc(A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc) -> 156: whats_up_calc(A1-1, A2+1, A3+2, A4+3, A5+4, A6+5, A7+6, A8+7, A9+8, [A1,A2|Acc]). 157: 158: %%% 159: %%% Testing a call to erlang:hibernate/3 that the compiler and loader do not 160: %%% translate to an instruction. 161: %%% 162: 163: dynamic_call(Config) when is_list(Config) -> 164: Ref = make_ref(), 165: Info = {self(),Ref}, 166: ExpectedHeapSz = erts_debug:size([Info]), 167: ?line Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end), 168: ?line hibernate_wake_up(100, ExpectedHeapSz, Child), 169: ?line Child ! please_quit_now, 170: ok. 171: 172: dynamic_call_hibernator(Info, Function) -> 173: {catchlevel,0} = process_info(self(), catchlevel), 174: receive 175: Any -> 176: dynamic_call_hibernator_msg(Any, Function, Info), 177: dynamic_call_hibernator(Info, Function) 178: end. 179: 180: dynamic_call_hibernator_msg({hibernate,_}, Function, Info) -> 181: catch apply(erlang, Function, [?MODULE, basic_hibernator, [Info]]), 182: exit(hibernate_returned); 183: dynamic_call_hibernator_msg(Msg, _Function, Info) -> 184: basic_hibernator_msg(Msg, Info). 185: 186: %%% 187: %%% Testing setting the minimum heap size. 188: %%% 189: 190: min_heap_size(Config) when is_list(Config) -> 191: case test_server:is_native(?MODULE) of 192: true -> {skip, "Test case relies on trace which is not available in HiPE"}; 193: false -> min_heap_size_1(Config) 194: end. 195: 196: min_heap_size_1(Config) when is_list(Config) -> 197: ?line erlang:trace(new, true, [call]), 198: MFA = {?MODULE,min_hibernator,1}, 199: ?line 1 = erlang:trace_pattern(MFA, true, [local]), 200: Ref = make_ref(), 201: Info = {self(),Ref}, 202: ?line Child = spawn_opt(fun() -> min_hibernator(Info) end, 203: [{min_heap_size,15000},link]), 204: receive 205: {trace,Child,call,{?MODULE,min_hibernator,_}} -> 206: ?line 1 = erlang:trace_pattern(MFA, false, [local]), 207: ?line erlang:trace(new, false, [call]) 208: end, 209: {heap_size,HeapSz} = process_info(Child, heap_size), 210: io:format("Heap size: ~p\n", [HeapSz]), 211: ?line if 212: HeapSz < 20 -> ok 213: end, 214: ?line Child ! wake_up, 215: receive 216: {heap_size,AfterSize} -> 217: io:format("Heap size after wakeup: ~p\n", [AfterSize]), 218: ?line 219: if 220: AfterSize >= 15000 -> ok 221: end; 222: Other -> 223: io:format("Unexpected: ~p\n", [Other]), 224: ?line ?t:fail() 225: end. 226: 227: min_hibernator({Parent,_Ref}) -> 228: erlang:hibernate(erlang, apply, [fun min_hibernator_recv/1, [Parent]]). 229: 230: min_hibernator_recv(Parent) -> 231: receive 232: wake_up -> 233: Parent ! process_info(self(), heap_size) 234: end. 235: 236: %%% 237: %%% Testing feeding erlang:hibernate/3 with bad arguments. 238: %%% 239: 240: bad_args(Config) when is_list(Config) -> 241: ?line bad_args(?MODULE, {name,glurf}, [0]), 242: ?line {'EXIT',{system_limit,_}} = 243: (catch erlang:hibernate(x, y, lists:duplicate(5122, xxx))), 244: ?line bad_args(42, name, [0]), 245: ?line bad_args(xx, 42, [1]), 246: ?line bad_args(xx, 42, glurf), 247: ?line bad_args(xx, 42, {}), 248: ?line bad_args({}, name, [2]), 249: ?line bad_args({1}, name, [3]), 250: ?line bad_args({1,2,3}, name, [4]), 251: ?line bad_args({1,2,3}, name, [5]), 252: ?line bad_args({1,2,3,4}, name, [6]), 253: ?line bad_args({1,2,3,4,5,6}, name,[7]), 254: ?line bad_args({1,2,3,4,5}, name, [8]), 255: ?line bad_args({1,2}, name, [9]), 256: ?line bad_args([1,2], name, [9]), 257: ?line bad_args(55.0, name, [9]), 258: ok. 259: 260: bad_args(Mod, Name, Args) -> 261: Res = (catch erlang:hibernate(Mod, Name, Args)), 262: erlang:garbage_collect(), 263: case Res of 264: {'EXIT',{badarg,_Where}} -> 265: io:format("erlang:hibernate(~p, ~p, ~p) -> ~p\n", [Mod,Name,Args,Res]); 266: Other -> 267: io:format("erlang:hibernate(~p, ~p, ~p) -> ~p\n", [Mod,Name,Args,Res]), 268: ?t:fail({bad_result,Other}) 269: end. 270: 271: 272: %%% 273: %%% Testing calling erlang:hibernate/3 with messages already in the message queue. 274: %%% 275: 276: messages_in_queue(Config) when is_list(Config) -> 277: Self = self(), 278: Msg = {Self,make_ref(),a,message}, 279: Pid = spawn_link(fun() -> messages_in_queue_1(Self, Msg) end), 280: Pid ! Msg, 281: Pid ! go_ahead, 282: receive 283: done -> ok; 284: Other -> 285: ?line io:format("~p\n", [Other]), 286: ?line ?t:fail(unexpected_message) 287: end. 288: 289: messages_in_queue_1(Parent, ExpectedMsg) -> 290: receive 291: go_ahead -> ok 292: end, 293: {message_queue_len,1} = process_info(self(), message_queue_len), 294: erlang:hibernate(?MODULE, messages_in_queue_restart, 295: [Parent,ExpectedMsg]). 296: 297: messages_in_queue_restart(Parent, ExpectedMessage) -> 298: ?line receive 299: ExpectedMessage -> 300: Parent ! done; 301: Other -> 302: io:format("~p\n", [Other]), 303: ?t:fail(unexpected_message) 304: end, 305: ok. 306: 307: 308: %%% 309: %%% Test that trying to hibernate to an undefined MFA gives the correct 310: %%% exit behavior. 311: %%% 312: 313: undefined_mfa(Config) when is_list(Config) -> 314: ?line process_flag(trap_exit, true), 315: ?line Pid = spawn_link(fun() -> 316: %% Will be a call_only instruction. 317: erlang:hibernate(?MODULE, blarf, []) end), 318: ?line Pid ! {a,message}, 319: ?line receive 320: {'EXIT',Pid,{undef,Undef}} -> 321: io:format("~p\n", [Undef]), 322: ok; 323: Other -> 324: ?line io:format("~p\n", [Other]), 325: ?line ?t:fail(unexpected_message) 326: end, 327: undefined_mfa_1(). 328: 329: undefined_mfa_1() -> 330: ?line Pid = spawn_link(fun() -> 331: %% Force a call_last instruction by calling bar() 332: %% (if that is not obvious). 333: bar(), 334: erlang:hibernate(?MODULE, blarf, []) 335: end), 336: ?line Pid ! {another,message}, 337: ?line receive 338: {'EXIT',Pid,{undef,Undef}} -> 339: io:format("~p\n", [Undef]), 340: ok; 341: Other -> 342: ?line io:format("~p\n", [Other]), 343: ?line ?t:fail(unexpected_message) 344: end, 345: ok. 346: 347: bar() -> 348: ok. 349: 350: %% 351: %% No heap 352: %% 353: 354: no_heap(doc) -> []; 355: no_heap(suite) -> []; 356: no_heap(Config) when is_list(Config) -> 357: ?line H = spawn_link(fun () -> clean_dict(), no_heap_loop() end), 358: ?line lists:foreach(fun (_) -> 359: wait_until(fun () -> is_hibernated(H) end), 360: ?line [{heap_size,1}, 361: {total_heap_size,1}] 362: = process_info(H, 363: [heap_size, 364: total_heap_size]), 365: receive after 10 -> ok end, 366: H ! again 367: end, 368: lists:seq(1, 100)), 369: ?line unlink(H), 370: ?line exit(H, bye). 371: 372: no_heap_loop() -> 373: flush(), 374: erlang:hibernate(?MODULE, no_heap_loop, []). 375: 376: clean_dict() -> 377: {dictionary, Dict} = process_info(self(), dictionary), 378: lists:foreach(fun ({Key, _}) -> erase(Key) end, Dict). 379: 380: %% 381: %% Wake up and then immediatly bif trap with a lengthy computation. 382: %% 383: 384: wake_up_and_bif_trap(doc) -> []; 385: wake_up_and_bif_trap(suite) -> []; 386: wake_up_and_bif_trap(Config) when is_list(Config) -> 387: ?line Self = self(), 388: ?line Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, characters_to_list_trap, [Self]) end), 389: ?line Pid ! wakeup, 390: ?line receive 391: {ok, Pid0} when Pid0 =:= Pid -> ok 392: after 5000 -> 393: ?line ?t:fail(process_blocked) 394: end, 395: ?line unlink(Pid), 396: ?line exit(Pid, bye). 397: 398: %% Lengthy computation that traps (in characters_to_list_trap_3). 399: characters_to_list_trap(Parent) -> 400: Bin0 = <<"abcdefghijklmnopqrstuvwxz0123456789">>, 401: Bin = binary:copy(Bin0, 1500), 402: unicode:characters_to_list(Bin), 403: Parent ! {ok, self()}. 404: 405: %% 406: %% Misc 407: %% 408: 409: is_hibernated(P) -> 410: case process_info(P, [current_function, status]) of 411: [{current_function, {erlang, hibernate, _}}, 412: {status, waiting}] -> 413: true; 414: _ -> 415: false 416: end. 417: 418: flush() -> 419: receive 420: _Msg -> flush() 421: after 0 -> 422: ok 423: end. 424: 425: 426: wait_until(Fun) -> 427: case catch Fun() of 428: true -> ok; 429: _ -> receive after 10 -> wait_until(Fun) end 430: end.