1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2020. 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%%%------------------------------------------------------------------- 23%%% File : scheduler_SUITE.erl 24%%% Author : Rickard Green 25%%% Description : 26%%% 27%%% Created : 27 Oct 2008 by Rickard Green 28%%%------------------------------------------------------------------- 29-module(scheduler_SUITE). 30 31 32%-define(line_trace, 1). 33 34-include_lib("common_test/include/ct.hrl"). 35 36%-compile(export_all). 37-export([all/0, suite/0, groups/0, 38 init_per_suite/1, end_per_suite/1, 39 init_per_testcase/2, end_per_testcase/2]). 40 41-export([equal/1, 42 few_low/1, 43 many_low/1, 44 equal_with_part_time_high/1, 45 equal_with_part_time_max/1, 46 equal_and_high_with_part_time_max/1, 47 equal_with_high/1, 48 equal_with_high_max/1, 49 bound_process/1, 50 51 scheduler_bind_types/1, 52 cpu_topology/1, 53 update_cpu_info/1, 54 sct_cmd/1, 55 sbt_cmd/1, 56 scheduler_threads/1, 57 scheduler_suspend_basic/1, 58 scheduler_suspend/1, 59 dirty_scheduler_threads/1, 60 poll_threads/1, 61 reader_groups/1, 62 otp_16446/1, 63 simultaneously_change_schedulers_online/1, 64 simultaneously_change_schedulers_online_with_exits/1]). 65 66suite() -> 67 [{ct_hooks,[ts_install_cth]}, 68 {timetrap, {minutes, 15}}]. 69 70all() -> 71 [equal, few_low, many_low, equal_with_part_time_high, 72 equal_with_part_time_max, 73 equal_and_high_with_part_time_max, equal_with_high, 74 equal_with_high_max, 75 bound_process, 76 {group, scheduler_bind}, scheduler_threads, 77 scheduler_suspend_basic, scheduler_suspend, 78 dirty_scheduler_threads, 79 poll_threads, 80 reader_groups, 81 otp_16446, 82 simultaneously_change_schedulers_online, 83 simultaneously_change_schedulers_online_with_exits]. 84 85groups() -> 86 [{scheduler_bind, [], 87 [scheduler_bind_types, cpu_topology, update_cpu_info, 88 sct_cmd, sbt_cmd]}]. 89 90init_per_suite(Config) -> 91 [{schedulers_online, erlang:system_info(schedulers_online)} | Config]. 92 93end_per_suite(Config) -> 94 catch erts_debug:set_internal_state(available_internal_state, false), 95 SchedOnln = proplists:get_value(schedulers_online, Config), 96 erlang:system_flag(schedulers_online, SchedOnln), 97 Config. 98 99init_per_testcase(update_cpu_info, Config) -> 100 case os:find_executable("taskset") of 101 false -> 102 {skip,"Could not find 'taskset' in path"}; 103 _ -> 104 init_per_tc(update_cpu_info, Config) 105 end; 106init_per_testcase(Case, Config) when is_list(Config) -> 107 init_per_tc(Case, Config). 108 109init_per_tc(Case, Config) -> 110 process_flag(priority, max), 111 erlang:display({'------------', ?MODULE, Case, '------------'}), 112 OkRes = ok, 113 [{testcase, Case}, {ok_res, OkRes} |Config]. 114 115end_per_testcase(_Case, Config) when is_list(Config) -> 116 ok. 117 118-define(ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED, (2000*2000)). 119-define(DEFAULT_TEST_REDS_PER_SCHED, 200000000). 120 121%% 122%% Test cases 123%% 124 125equal(Config) when is_list(Config) -> 126 low_normal_test(Config, 500, 500). 127 128few_low(Config) when is_list(Config) -> 129 low_normal_test(Config, 1000, 2*active_schedulers()). 130 131many_low(Config) when is_list(Config) -> 132 low_normal_test(Config, 2*active_schedulers(), 1000). 133 134low_normal_test(Config, NW, LW) -> 135 Tracer = start_tracer(), 136 Low = workers(LW, low), 137 Normal = workers(NW, normal), 138 Res = do_it(Tracer, Low, Normal, [], []), 139 chk_result(Res, LW, NW, 0, 0, true, false, false), 140 workers_exit([Low, Normal]), 141 ok(Res, Config). 142 143equal_with_part_time_high(Config) when is_list(Config) -> 144 NW = 500, 145 LW = 500, 146 HW = 1, 147 Tracer = start_tracer(), 148 Normal = workers(NW, normal), 149 Low = workers(LW, low), 150 High = part_time_workers(HW, high), 151 Res = do_it(Tracer, Low, Normal, High, []), 152 chk_result(Res, LW, NW, HW, 0, true, true, false), 153 workers_exit([Low, Normal, High]), 154 ok(Res, Config). 155 156equal_and_high_with_part_time_max(Config) when is_list(Config) -> 157 NW = 500, 158 LW = 500, 159 HW = 500, 160 MW = 1, 161 Tracer = start_tracer(), 162 Low = workers(LW, low), 163 Normal = workers(NW, normal), 164 High = workers(HW, high), 165 Max = part_time_workers(MW, max), 166 Res = do_it(Tracer, Low, Normal, High, Max), 167 chk_result(Res, LW, NW, HW, MW, false, true, true), 168 workers_exit([Low, Normal, Max]), 169 ok(Res, Config). 170 171equal_with_part_time_max(Config) when is_list(Config) -> 172 NW = 500, 173 LW = 500, 174 MW = 1, 175 Tracer = start_tracer(), 176 Low = workers(LW, low), 177 Normal = workers(NW, normal), 178 Max = part_time_workers(MW, max), 179 Res = do_it(Tracer, Low, Normal, [], Max), 180 chk_result(Res, LW, NW, 0, MW, true, false, true), 181 workers_exit([Low, Normal, Max]), 182 ok(Res, Config). 183 184equal_with_high(Config) when is_list(Config) -> 185 NW = 500, 186 LW = 500, 187 HW = 1, 188 Tracer = start_tracer(), 189 Low = workers(LW, low), 190 Normal = workers(NW, normal), 191 High = workers(HW, high), 192 Res = do_it(Tracer, Low, Normal, High, []), 193 LNExe = case active_schedulers() of 194 S when S =< HW -> false; 195 _ -> true 196 end, 197 chk_result(Res, LW, NW, HW, 0, LNExe, true, false), 198 workers_exit([Low, Normal, High]), 199 ok(Res, Config). 200 201equal_with_high_max(Config) when is_list(Config) -> 202 NW = 500, 203 LW = 500, 204 HW = 1, 205 MW = 1, 206 Tracer = start_tracer(), 207 Normal = workers(NW, normal), 208 Low = workers(LW, low), 209 High = workers(HW, high), 210 Max = workers(MW, max), 211 Res = do_it(Tracer, Low, Normal, High, Max), 212 {LNExe, HExe} = case active_schedulers() of 213 S when S =< MW -> {false, false}; 214 S when S =< (MW + HW) -> {false, true}; 215 _ -> {true, true} 216 end, 217 chk_result(Res, LW, NW, HW, MW, LNExe, HExe, true), 218 workers_exit([Low, Normal, Max]), 219 ok(Res, Config). 220 221bound_process(Config) when is_list(Config) -> 222 case erlang:system_info(run_queues) == erlang:system_info(schedulers) of 223 true -> 224 NStartBase = 20000, 225 NStart = case {erlang:system_info(debug_compiled), 226 erlang:system_info(lock_checking)} of 227 {true, true} -> NStartBase div 100; 228 {_, true} -> NStartBase div 10; 229 _ -> NStartBase 230 end, 231 MStart = 100, 232 Seq = lists:seq(1, 100), 233 Tester = self(), 234 Procs = lists:map( 235 fun (N) when N rem 2 == 0 -> 236 spawn_opt(fun () -> 237 bound_loop(NStart, 238 NStart, 239 MStart, 240 1), 241 Tester ! {self(), done} 242 end, 243 [{scheduler, 1}, link]); 244 (_N) -> 245 spawn_link(fun () -> 246 bound_loop(NStart, 247 NStart, 248 MStart, 249 false), 250 Tester ! {self(), done} 251 end) 252 end, 253 Seq), 254 lists:foreach(fun (P) -> receive {P, done} -> ok end end, 255 Procs), 256 ok; 257 false -> 258 {skipped, "Functionality not supported"} 259 end. 260 261bound_loop(_, 0, 0, _) -> 262 ok; 263bound_loop(NS, 0, M, false) -> 264 bound_loop(NS, NS, M-1, false); 265bound_loop(NS, N, M, false) -> 266 erlang:system_info(scheduler_id), 267 bound_loop(NS, N-1, M, false); 268bound_loop(NS, 0, M, Sched) -> 269 NewSched = (Sched rem erlang:system_info(schedulers_online)) + 1, 270 Sched = process_flag(scheduler, NewSched), 271 NewSched = erlang:system_info(scheduler_id), 272 bound_loop(NS, NS, M-1, NewSched); 273bound_loop(NS, N, M, Sched) -> 274 Sched = erlang:system_info(scheduler_id), 275 bound_loop(NS, N-1, M, Sched). 276 277 278-define(TOPOLOGY_A_CMD, 279 "+sct" 280 "L0-1t0-1c0p0n0" 281 ":L2-3t0-1c1p0n0" 282 ":L4-5t0-1c0p1n0" 283 ":L6-7t0-1c1p1n0" 284 ":L8-9t0-1c0p2n1" 285 ":L10-11t0-1c1p2n1" 286 ":L12-13t0-1c0p3n1" 287 ":L14-15t0-1c1p3n1"). 288 289-define(TOPOLOGY_A_TERM, 290 [{node,[{processor,[{core,[{thread,{logical,0}}, 291 {thread,{logical,1}}]}, 292 {core,[{thread,{logical,2}}, 293 {thread,{logical,3}}]}]}, 294 {processor,[{core,[{thread,{logical,4}}, 295 {thread,{logical,5}}]}, 296 {core,[{thread,{logical,6}}, 297 {thread,{logical,7}}]}]}]}, 298 {node,[{processor,[{core,[{thread,{logical,8}}, 299 {thread,{logical,9}}]}, 300 {core,[{thread,{logical,10}}, 301 {thread,{logical,11}}]}]}, 302 {processor,[{core,[{thread,{logical,12}}, 303 {thread,{logical,13}}]}, 304 {core,[{thread,{logical,14}}, 305 {thread,{logical,15}}]}]}]}]). 306 307-define(TOPOLOGY_B_CMD, 308 "+sct" 309 "L0-1t0-1c0n0p0" 310 ":L2-3t0-1c1n0p0" 311 ":L4-5t0-1c2n1p0" 312 ":L6-7t0-1c3n1p0" 313 ":L8-9t0-1c0n2p1" 314 ":L10-11t0-1c1n2p1" 315 ":L12-13t0-1c2n3p1" 316 ":L14-15t0-1c3n3p1"). 317 318-define(TOPOLOGY_B_TERM, 319 [{processor,[{node,[{core,[{thread,{logical,0}}, 320 {thread,{logical,1}}]}, 321 {core,[{thread,{logical,2}}, 322 {thread,{logical,3}}]}]}, 323 {node,[{core,[{thread,{logical,4}}, 324 {thread,{logical,5}}]}, 325 {core,[{thread,{logical,6}}, 326 {thread,{logical,7}}]}]}]}, 327 {processor,[{node,[{core,[{thread,{logical,8}}, 328 {thread,{logical,9}}]}, 329 {core,[{thread,{logical,10}}, 330 {thread,{logical,11}}]}]}, 331 {node,[{core,[{thread,{logical,12}}, 332 {thread,{logical,13}}]}, 333 {core,[{thread,{logical,14}}, 334 {thread,{logical,15}}]}]}]}]). 335 336-define(TOPOLOGY_C_TERM, 337 [{node,[{processor,[{core,[{thread,{logical,0}}, 338 {thread,{logical,1}}]}, 339 {core,[{thread,{logical,2}}, 340 {thread,{logical,3}}]}]}, 341 {processor,[{core,[{thread,{logical,4}}, 342 {thread,{logical,5}}]}, 343 {core,[{thread,{logical,6}}, 344 {thread,{logical,7}}]}]}]}, 345 {processor,[{node,[{core,[{thread,{logical,8}}, 346 {thread,{logical,9}}]}, 347 {core,[{thread,{logical,10}}, 348 {thread,{logical,11}}]}]}, 349 {node,[{core,[{thread,{logical,12}}, 350 {thread,{logical,13}}]}, 351 {core,[{thread,{logical,14}}, 352 {thread,{logical,15}}]}]}]}, 353 {node,[{processor,[{core,[{thread,{logical,16}}, 354 {thread,{logical,17}}]}, 355 {core,[{thread,{logical,18}}, 356 {thread,{logical,19}}]}]}, 357 {processor,[{core,[{thread,{logical,20}}, 358 {thread,{logical,21}}]}, 359 {core,[{thread,{logical,22}}, 360 {thread,{logical,23}}]}]}]}, 361 {processor,[{node,[{core,[{thread,{logical,24}}, 362 {thread,{logical,25}}]}, 363 {core,[{thread,{logical,26}}, 364 {thread,{logical,27}}]}]}, 365 {node,[{core,[{thread,{logical,28}}, 366 {thread,{logical,29}}]}, 367 {core,[{thread,{logical,30}}, 368 {thread,{logical,31}}]}]}]}]). 369 370 371-define(TOPOLOGY_C_CMD, 372 "+sct" 373 "L0-1t0-1c0p0n0" 374 ":L2-3t0-1c1p0n0" 375 ":L4-5t0-1c0p1n0" 376 ":L6-7t0-1c1p1n0" 377 ":L8-9t0-1c0n1p2" 378 ":L10-11t0-1c1n1p2" 379 ":L12-13t0-1c2n2p2" 380 ":L14-15t0-1c3n2p2" 381 ":L16-17t0-1c0p3n3" 382 ":L18-19t0-1c1p3n3" 383 ":L20-21t0-1c0p4n3" 384 ":L22-23t0-1c1p4n3" 385 ":L24-25t0-1c0n4p5" 386 ":L26-27t0-1c1n4p5" 387 ":L28-29t0-1c2n5p5" 388 ":L30-31t0-1c3n5p5"). 389 390-define(TOPOLOGY_D_TERM, 391 [{processor,[{node,[{core,[{thread,{logical,0}}, 392 {thread,{logical,1}}]}, 393 {core,[{thread,{logical,2}}, 394 {thread,{logical,3}}]}]}, 395 {node,[{core,[{thread,{logical,4}}, 396 {thread,{logical,5}}]}, 397 {core,[{thread,{logical,6}}, 398 {thread,{logical,7}}]}]}]}, 399 {node,[{processor,[{core,[{thread,{logical,8}}, 400 {thread,{logical,9}}]}, 401 {core,[{thread,{logical,10}}, 402 {thread,{logical,11}}]}]}, 403 {processor,[{core,[{thread,{logical,12}}, 404 {thread,{logical,13}}]}, 405 {core,[{thread,{logical,14}}, 406 {thread,{logical,15}}]}]}]}, 407 {processor,[{node,[{core,[{thread,{logical,16}}, 408 {thread,{logical,17}}]}, 409 {core,[{thread,{logical,18}}, 410 {thread,{logical,19}}]}]}, 411 {node,[{core,[{thread,{logical,20}}, 412 {thread,{logical,21}}]}, 413 {core,[{thread,{logical,22}}, 414 {thread,{logical,23}}]}]}]}, 415 {node,[{processor,[{core,[{thread,{logical,24}}, 416 {thread,{logical,25}}]}, 417 {core,[{thread,{logical,26}}, 418 {thread,{logical,27}}]}]}, 419 {processor,[{core,[{thread,{logical,28}}, 420 {thread,{logical,29}}]}, 421 {core,[{thread,{logical,30}}, 422 {thread,{logical,31}}]}]}]}]). 423 424-define(TOPOLOGY_D_CMD, 425 "+sct" 426 "L0-1t0-1c0n0p0" 427 ":L2-3t0-1c1n0p0" 428 ":L4-5t0-1c2n1p0" 429 ":L6-7t0-1c3n1p0" 430 ":L8-9t0-1c0p1n2" 431 ":L10-11t0-1c1p1n2" 432 ":L12-13t0-1c0p2n2" 433 ":L14-15t0-1c1p2n2" 434 ":L16-17t0-1c0n3p3" 435 ":L18-19t0-1c1n3p3" 436 ":L20-21t0-1c2n4p3" 437 ":L22-23t0-1c3n4p3" 438 ":L24-25t0-1c0p4n5" 439 ":L26-27t0-1c1p4n5" 440 ":L28-29t0-1c0p5n5" 441 ":L30-31t0-1c1p5n5"). 442 443-define(TOPOLOGY_E_CMD, 444 "+sct" 445 "L0-1t0-1c0p0n0" 446 ":L2-3t0-1c1p0n0" 447 ":L4-5t0-1c2p0n0" 448 ":L6-7t0-1c3p0n0" 449 ":L8-9t0-1c0p1n1" 450 ":L10-11t0-1c1p1n1" 451 ":L12-13t0-1c2p1n1" 452 ":L14-15t0-1c3p1n1"). 453 454-define(TOPOLOGY_E_TERM, 455 [{node,[{processor,[{core,[{thread,{logical,0}}, 456 {thread,{logical,1}}]}, 457 {core,[{thread,{logical,2}}, 458 {thread,{logical,3}}]}, 459 {core,[{thread,{logical,4}}, 460 {thread,{logical,5}}]}, 461 {core,[{thread,{logical,6}}, 462 {thread,{logical,7}}]}]}]}, 463 {node,[{processor,[{core,[{thread,{logical,8}}, 464 {thread,{logical,9}}]}, 465 {core,[{thread,{logical,10}}, 466 {thread,{logical,11}}]}, 467 {core,[{thread,{logical,12}}, 468 {thread,{logical,13}}]}, 469 {core,[{thread,{logical,14}}, 470 {thread,{logical,15}}]}]}]}]). 471 472-define(TOPOLOGY_F_CMD, 473 "+sct" 474 "L0-1t0-1c0n0p0" 475 ":L2-3t0-1c1n0p0" 476 ":L4-5t0-1c2n0p0" 477 ":L6-7t0-1c3n0p0" 478 ":L8-9t0-1c4n1p0" 479 ":L10-11t0-1c5n1p0" 480 ":L12-13t0-1c6n1p0" 481 ":L14-15t0-1c7n1p0" 482 ":L16-17t0-1c8n2p0" 483 ":L18-19t0-1c9n2p0" 484 ":L20-21t0-1c10n2p0" 485 ":L22-23t0-1c11n2p0" 486 ":L24-25t0-1c12n3p0" 487 ":L26-27t0-1c13n3p0" 488 ":L28-29t0-1c14n3p0" 489 ":L30-31t0-1c15n3p0"). 490 491-define(TOPOLOGY_F_TERM, 492 [{processor,[{node,[{core,[{thread,{logical,0}}, 493 {thread,{logical,1}}]}, 494 {core,[{thread,{logical,2}}, 495 {thread,{logical,3}}]}, 496 {core,[{thread,{logical,4}}, 497 {thread,{logical,5}}]}, 498 {core,[{thread,{logical,6}}, 499 {thread,{logical,7}}]}]}, 500 {node,[{core,[{thread,{logical,8}}, 501 {thread,{logical,9}}]}, 502 {core,[{thread,{logical,10}}, 503 {thread,{logical,11}}]}, 504 {core,[{thread,{logical,12}}, 505 {thread,{logical,13}}]}, 506 {core,[{thread,{logical,14}}, 507 {thread,{logical,15}}]}]}, 508 {node,[{core,[{thread,{logical,16}}, 509 {thread,{logical,17}}]}, 510 {core,[{thread,{logical,18}}, 511 {thread,{logical,19}}]}, 512 {core,[{thread,{logical,20}}, 513 {thread,{logical,21}}]}, 514 {core,[{thread,{logical,22}}, 515 {thread,{logical,23}}]}]}, 516 {node,[{core,[{thread,{logical,24}}, 517 {thread,{logical,25}}]}, 518 {core,[{thread,{logical,26}}, 519 {thread,{logical,27}}]}, 520 {core,[{thread,{logical,28}}, 521 {thread,{logical,29}}]}, 522 {core,[{thread,{logical,30}}, 523 {thread,{logical,31}}]}]}]}]). 524 525bindings(Node, BindType) -> 526 Parent = self(), 527 Ref = make_ref(), 528 Pid = spawn_link(Node, 529 fun () -> 530 enable_internal_state(), 531 Res = (catch erts_debug:get_internal_state( 532 {fake_scheduler_bindings, 533 BindType})), 534 Parent ! {Ref, Res} 535 end), 536 receive 537 {Ref, Res} -> 538 io:format("~p: ~p~n", [BindType, Res]), 539 unlink(Pid), 540 Res 541 end. 542 543scheduler_bind_types(Config) when is_list(Config) -> 544 OldRelFlags = clear_erl_rel_flags(), 545 try 546 scheduler_bind_types_test(Config, 547 ?TOPOLOGY_A_TERM, 548 ?TOPOLOGY_A_CMD, 549 a), 550 scheduler_bind_types_test(Config, 551 ?TOPOLOGY_B_TERM, 552 ?TOPOLOGY_B_CMD, 553 b), 554 scheduler_bind_types_test(Config, 555 ?TOPOLOGY_C_TERM, 556 ?TOPOLOGY_C_CMD, 557 c), 558 scheduler_bind_types_test(Config, 559 ?TOPOLOGY_D_TERM, 560 ?TOPOLOGY_D_CMD, 561 d), 562 scheduler_bind_types_test(Config, 563 ?TOPOLOGY_E_TERM, 564 ?TOPOLOGY_E_CMD, 565 e), 566 scheduler_bind_types_test(Config, 567 ?TOPOLOGY_F_TERM, 568 ?TOPOLOGY_F_CMD, 569 f) 570 after 571 restore_erl_rel_flags(OldRelFlags) 572 end, 573 ok. 574 575scheduler_bind_types_test(Config, Topology, CmdLine, TermLetter) -> 576 io:format("Testing (~p): ~p~n", [TermLetter, Topology]), 577 {ok, Node0} = start_node(Config), 578 _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]), 579 cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])), 580 check_bind_types(Node0, TermLetter), 581 stop_node(Node0), 582 {ok, Node1} = start_node(Config, CmdLine), 583 cmp(Topology, rpc:call(Node1, erlang, system_info, [cpu_topology])), 584 check_bind_types(Node1, TermLetter), 585 stop_node(Node1). 586 587check_bind_types(Node, a) -> 588 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} 589 = bindings(Node, no_spread), 590 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 591 = bindings(Node, thread_spread), 592 {0,4,8,12,2,6,10,14,1,5,9,13,3,7,11,15} 593 = bindings(Node, processor_spread), 594 {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15} 595 = bindings(Node, spread), 596 {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} 597 = bindings(Node, no_node_thread_spread), 598 {0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15} 599 = bindings(Node, no_node_processor_spread), 600 {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15} 601 = bindings(Node, thread_no_node_processor_spread), 602 {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15} 603 = bindings(Node, default_bind), 604 ok; 605check_bind_types(Node, b) -> 606 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} 607 = bindings(Node, no_spread), 608 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 609 = bindings(Node, thread_spread), 610 {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} 611 = bindings(Node, processor_spread), 612 {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15} 613 = bindings(Node, spread), 614 {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15} 615 = bindings(Node, no_node_thread_spread), 616 {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15} 617 = bindings(Node, no_node_processor_spread), 618 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 619 = bindings(Node, thread_no_node_processor_spread), 620 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 621 = bindings(Node, default_bind), 622 ok; 623check_bind_types(Node, c) -> 624 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 625 25,26,27,28,29,30,31} = bindings(Node, no_spread), 626 {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, 627 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), 628 {0,4,8,16,20,24,2,6,10,18,22,26,12,28,14,30,1,5,9,17,21,25, 629 3,7,11,19,23,27,13,29,15,31} = bindings(Node, processor_spread), 630 {0,8,16,24,4,20,12,28,2,10,18,26,6,22,14,30,1,9,17,25,5,21,13,29,3,11, 631 19,27,7,23,15,31} = bindings(Node, spread), 632 {0,2,4,6,1,3,5,7,8,10,9,11,12,14,13,15,16,18,20,22,17,19,21,23,24,26, 633 25,27,28,30,29,31} = bindings(Node, no_node_thread_spread), 634 {0,4,2,6,1,5,3,7,8,10,9,11,12,14,13,15,16,20,18,22,17,21,19,23,24,26, 635 25,27,28,30,29,31} = bindings(Node, no_node_processor_spread), 636 {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21, 637 19,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread), 638 {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21, 639 19,23,25,27,29,31} = bindings(Node, default_bind), 640 ok; 641check_bind_types(Node, d) -> 642 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 643 25,26,27,28,29,30,31} = bindings(Node, no_spread), 644 {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, 645 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), 646 {0,8,12,16,24,28,2,10,14,18,26,30,4,20,6,22,1,9,13,17,25,29,3,11,15, 647 19,27,31,5,21,7,23} = bindings(Node, processor_spread), 648 {0,8,16,24,12,28,4,20,2,10,18,26,14,30,6,22,1,9,17,25,13,29,5,21,3,11, 649 19,27,15,31,7,23} = bindings(Node, spread), 650 {0,2,1,3,4,6,5,7,8,10,12,14,9,11,13,15,16,18,17,19,20,22,21,23,24,26, 651 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread), 652 {0,2,1,3,4,6,5,7,8,12,10,14,9,13,11,15,16,18,17,19,20,22,21,23,24,28, 653 26,30,25,29,27,31} = bindings(Node, no_node_processor_spread), 654 {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19, 655 21,23,25,29,27,31} = bindings(Node, thread_no_node_processor_spread), 656 {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19, 657 21,23,25,29,27,31} = bindings(Node, default_bind), 658 ok; 659check_bind_types(Node, e) -> 660 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} 661 = bindings(Node, no_spread), 662 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 663 = bindings(Node, thread_spread), 664 {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} 665 = bindings(Node, processor_spread), 666 {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} 667 = bindings(Node, spread), 668 {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} 669 = bindings(Node, no_node_thread_spread), 670 {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} 671 = bindings(Node, no_node_processor_spread), 672 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 673 = bindings(Node, thread_no_node_processor_spread), 674 {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} 675 = bindings(Node, default_bind), 676 ok; 677check_bind_types(Node, f) -> 678 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 679 25,26,27,28,29,30,31} = bindings(Node, no_spread), 680 {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, 681 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), 682 {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13, 683 15,17,19,21,23,25,27,29,31} = bindings(Node, processor_spread), 684 {0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30,1,9,17,25,3,11,19,27,5,13, 685 21,29,7,15,23,31} = bindings(Node, spread), 686 {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26, 687 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread), 688 {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26, 689 28,30,25,27,29,31} = bindings(Node, no_node_processor_spread), 690 {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19, 691 21,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread), 692 {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19, 693 21,23,25,27,29,31} = bindings(Node, default_bind), 694 ok; 695check_bind_types(Node, _) -> 696 bindings(Node, no_spread), 697 bindings(Node, thread_spread), 698 bindings(Node, processor_spread), 699 bindings(Node, spread), 700 bindings(Node, no_node_thread_spread), 701 bindings(Node, no_node_processor_spread), 702 bindings(Node, thread_no_node_processor_spread), 703 bindings(Node, default_bind), 704 ok. 705 706cpu_topology(Config) when is_list(Config) -> 707 OldRelFlags = clear_erl_rel_flags(), 708 try 709 cpu_topology_test( 710 Config, 711 [{node,[{processor,[{core,{logical,0}}, 712 {core,{logical,1}}]}]}, 713 {processor,[{node,[{core,{logical,2}}, 714 {core,{logical,3}}]}]}, 715 {node,[{processor,[{core,{logical,4}}, 716 {core,{logical,5}}]}]}, 717 {processor,[{node,[{core,{logical,6}}, 718 {core,{logical,7}}]}]}], 719 "+sct " 720 "L0-1c0-1p0n0" 721 ":L2-3c0-1n1p1" 722 ":L4-5c0-1p2n2" 723 ":L6-7c0-1n3p3"), 724 cpu_topology_test( 725 Config, 726 [{node,[{processor,[{core,{logical,0}}, 727 {core,{logical,1}}]}, 728 {processor,[{core,{logical,2}}, 729 {core,{logical,3}}]}]}, 730 {processor,[{node,[{core,{logical,4}}, 731 {core,{logical,5}}]}, 732 {node,[{core,{logical,6}}, 733 {core,{logical,7}}]}]}, 734 {node,[{processor,[{core,{logical,8}}, 735 {core,{logical,9}}]}, 736 {processor,[{core,{logical,10}}, 737 {core,{logical,11}}]}]}, 738 {processor,[{node,[{core,{logical,12}}, 739 {core,{logical,13}}]}, 740 {node,[{core,{logical,14}}, 741 {core,{logical,15}}]}]}], 742 "+sct " 743 "L0-1c0-1p0n0" 744 ":L2-3c0-1p1n0" 745 ":L4-5c0-1n1p2" 746 ":L6-7c2-3n2p2" 747 ":L8-9c0-1p3n3" 748 ":L10-11c0-1p4n3" 749 ":L12-13c0-1n4p5" 750 ":L14-15c2-3n5p5"), 751 cpu_topology_test( 752 Config, 753 [{node,[{processor,[{core,{logical,0}}, 754 {core,{logical,1}}]}]}, 755 {processor,[{node,[{core,{logical,2}}, 756 {core,{logical,3}}]}]}, 757 {processor,[{node,[{core,{logical,4}}, 758 {core,{logical,5}}]}]}, 759 {node,[{processor,[{core,{logical,6}}, 760 {core,{logical,7}}]}]}, 761 {node,[{processor,[{core,{logical,8}}, 762 {core,{logical,9}}]}]}, 763 {processor,[{node,[{core,{logical,10}}, 764 {core,{logical,11}}]}]}], 765 "+sct " 766 "L0-1c0-1p0n0" 767 ":L2-3c0-1n1p1" 768 ":L4-5c0-1n2p2" 769 ":L6-7c0-1p3n3" 770 ":L8-9c0-1p4n4" 771 ":L10-11c0-1n5p5") 772 after 773 restore_erl_rel_flags(OldRelFlags) 774 end, 775 ok. 776 777cpu_topology_test(Config, Topology, Cmd) -> 778 io:format("Testing~n ~p~n ~p~n", [Topology, Cmd]), 779 cpu_topology_bif_test(Config, Topology), 780 cpu_topology_cmdline_test(Config, Topology, Cmd), 781 ok. 782 783cpu_topology_bif_test(_Config, false) -> 784 ok; 785cpu_topology_bif_test(Config, Topology) -> 786 {ok, Node} = start_node(Config), 787 _ = rpc:call(Node, erlang, system_flag, [cpu_topology, Topology]), 788 cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), 789 stop_node(Node), 790 ok. 791 792cpu_topology_cmdline_test(_Config, _Topology, false) -> 793 ok; 794cpu_topology_cmdline_test(Config, Topology, Cmd) -> 795 {ok, Node} = start_node(Config, Cmd), 796 cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), 797 stop_node(Node), 798 ok. 799 800update_cpu_info(Config) when is_list(Config) -> 801 OldOnline = erlang:system_info(schedulers_online), 802 OldAff = get_affinity_mask(), 803 io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", 804 [OldAff, OldOnline, erlang:system_info(scheduler_bindings)]), 805 case {erlang:system_info(logical_processors_available), OldAff} of 806 {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 -> 807 %% Nothing much to test; just a smoke test 808 case erlang:system_info(update_cpu_info) of 809 unchanged -> ok; 810 changed -> ok 811 end; 812 {_Avail, _} -> 813 try 814 adjust_schedulers_online(), 815 case erlang:system_info(schedulers_online) of 816 1 -> 817 %% Nothing much to test; just a smoke test 818 ok; 819 Onln0 -> 820 Cpus = bits_in_mask(OldAff), 821 RmCpus = case Cpus > Onln0 of 822 true -> Cpus - Onln0 + 1; 823 false -> Onln0 - Cpus + 1 824 end, 825 Onln1 = Cpus - RmCpus, 826 case Onln1 > 0 of 827 false -> 828 %% Nothing much to test; just a smoke test 829 ok; 830 true -> 831 Aff = restrict_affinity_mask(OldAff, RmCpus), 832 set_affinity_mask(Aff), 833 case adjust_schedulers_online() of 834 {Onln0, Onln1} -> 835 Onln1 = erlang:system_info(schedulers_online), 836 receive after 500 -> ok end, 837 io:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", 838 [Aff, Onln1, erlang:system_info(scheduler_bindings)]), 839 unchanged = adjust_schedulers_online(), 840 ok; 841 Fail -> 842 ct:fail(Fail) 843 end 844 end 845 end 846 after 847 set_affinity_mask(OldAff), 848 adjust_schedulers_online(), 849 erlang:system_flag(schedulers_online, OldOnline), 850 receive after 500 -> ok end, 851 io:format("END - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", 852 [get_affinity_mask(), 853 erlang:system_info(schedulers_online), 854 erlang:system_info(scheduler_bindings)]) 855 end 856 end. 857 858bits_in_mask(Mask) -> 859 bits_in_mask(Mask, 0, 0). 860 861bits_in_mask(0, _Shift, N) -> 862 N; 863bits_in_mask(Mask, Shift, N) -> 864 case Mask band (1 bsl Shift) of 865 0 -> bits_in_mask(Mask, Shift+1, N); 866 _ -> bits_in_mask(Mask band (bnot (1 bsl Shift)), 867 Shift+1, N+1) 868 end. 869 870restrict_affinity_mask(Mask, N) -> 871 try 872 restrict_affinity_mask(Mask, 0, N) 873 catch 874 throw : Reason -> 875 exit({Reason, Mask, N}) 876 end. 877 878restrict_affinity_mask(Mask, _Shift, 0) -> 879 Mask; 880restrict_affinity_mask(0, _Shift, _N) -> 881 throw(overresticted_affinity_mask); 882restrict_affinity_mask(Mask, Shift, N) -> 883 case Mask band (1 bsl Shift) of 884 0 -> restrict_affinity_mask(Mask, Shift+1, N); 885 _ -> restrict_affinity_mask(Mask band (bnot (1 bsl Shift)), 886 Shift+1, N-1) 887 end. 888 889adjust_schedulers_online() -> 890 case erlang:system_info(update_cpu_info) of 891 unchanged -> 892 unchanged; 893 changed -> 894 Avail = erlang:system_info(logical_processors_available), 895 Scheds = erlang:system_info(schedulers), 896 SOnln = case Avail > Scheds of 897 true -> Scheds; 898 false -> Avail 899 end, 900 {erlang:system_flag(schedulers_online, SOnln), SOnln} 901 end. 902 903read_affinity(Data) -> 904 Exp = "pid " ++ os:getpid() ++ "'s current affinity mask", 905 case string:lexemes(Data, ":") of 906 [Exp, DirtyAffinityStr] -> 907 AffinityStr = string:trim(DirtyAffinityStr), 908 case catch erlang:list_to_integer(AffinityStr, 16) of 909 Affinity when is_integer(Affinity) -> 910 Affinity; 911 _ -> 912 bad 913 end; 914 _ -> 915 bad 916 end. 917 918get_affinity_mask(Port, Status, Affinity) when Status == unknown; 919 Affinity == unknown -> 920 receive 921 {Port,{data, Data}} -> 922 get_affinity_mask(Port, Status, read_affinity(Data)); 923 {Port,{exit_status,S}} -> 924 get_affinity_mask(Port, S, Affinity) 925 end; 926get_affinity_mask(_Port, _Status, bad) -> 927 unknown; 928get_affinity_mask(_Port, _Status, Affinity) -> 929 Affinity. 930 931get_affinity_mask() -> 932 case os:type() of 933 {unix, linux} -> 934 case catch open_port({spawn, "taskset -p " ++ os:getpid()}, 935 [exit_status]) of 936 Port when is_port(Port) -> 937 get_affinity_mask(Port, unknown, unknown); 938 _ -> 939 unknown 940 end; 941 _ -> 942 unknown 943 end. 944 945set_affinity_mask(Port, unknown) -> 946 receive 947 {Port,{data, _}} -> 948 set_affinity_mask(Port, unknown); 949 {Port,{exit_status,Status}} -> 950 set_affinity_mask(Port, Status) 951 end; 952set_affinity_mask(Port, Status) -> 953 receive 954 {Port,{data, _}} -> 955 set_affinity_mask(Port, unknown) 956 after 0 -> 957 Status 958 end. 959 960set_affinity_mask(Mask) -> 961 Cmd = lists:flatten(["taskset -p ", 962 io_lib:format("~.16b", [Mask]), 963 " ", 964 os:getpid()]), 965 case catch open_port({spawn, Cmd}, [exit_status]) of 966 Port when is_port(Port) -> 967 case set_affinity_mask(Port, unknown) of 968 0 -> ok; 969 _ -> exit(failed_to_set_affinity) 970 end; 971 _ -> 972 exit(failed_to_set_affinity) 973 end. 974 975sct_cmd(Config) when is_list(Config) -> 976 Topology = ?TOPOLOGY_A_TERM, 977 OldRelFlags = clear_erl_rel_flags(), 978 try 979 {ok, Node} = start_node(Config, ?TOPOLOGY_A_CMD), 980 cmp(Topology, 981 rpc:call(Node, erlang, system_info, [cpu_topology])), 982 cmp(Topology, 983 rpc:call(Node, erlang, system_flag, [cpu_topology, Topology])), 984 cmp(Topology, 985 rpc:call(Node, erlang, system_info, [cpu_topology])), 986 stop_node(Node) 987 after 988 restore_erl_rel_flags(OldRelFlags) 989 end, 990 ok. 991 992-define(BIND_TYPES, 993 [{"u", unbound}, 994 {"ns", no_spread}, 995 {"ts", thread_spread}, 996 {"ps", processor_spread}, 997 {"s", spread}, 998 {"nnts", no_node_thread_spread}, 999 {"nnps", no_node_processor_spread}, 1000 {"tnnps", thread_no_node_processor_spread}, 1001 {"db", thread_no_node_processor_spread}]). 1002 1003sbt_cmd(Config) when is_list(Config) -> 1004 case sbt_check_prereqs() of 1005 {skipped, _Reason}=Skipped -> 1006 Skipped; 1007 ok -> 1008 case sbt_make_topology_args() of 1009 false -> 1010 {skipped, "Don't know how to create cpu topology"}; 1011 CpuTCmd -> 1012 LP = erlang:system_info(logical_processors), 1013 OldRelFlags = clear_erl_rel_flags(), 1014 try 1015 lists:foreach(fun ({ClBt, Bt}) -> 1016 sbt_test(Config, CpuTCmd, 1017 ClBt, Bt, LP) 1018 end, 1019 ?BIND_TYPES) 1020 after 1021 restore_erl_rel_flags(OldRelFlags) 1022 end, 1023 ok 1024 end 1025 end. 1026 1027sbt_make_topology_args() -> 1028 case erlang:system_info({cpu_topology,detected}) of 1029 undefined -> 1030 case os:type() of 1031 linux -> 1032 case erlang:system_info(logical_processors) of 1033 1 -> 1034 "+sctL0"; 1035 N -> 1036 NS = integer_to_list(N - 1), 1037 "+sctL0-"++NS++"p0-"++NS 1038 end; 1039 _ -> 1040 false 1041 end; 1042 _ -> 1043 "" 1044 end. 1045 1046sbt_check_prereqs() -> 1047 try 1048 Available = erlang:system_info(logical_processors_available), 1049 Quota = erlang:system_info(cpu_quota), 1050 if 1051 Quota =:= unknown; Quota >= Available -> 1052 ok; 1053 Quota < Available -> 1054 throw({skipped, "Test requires that CPU quota is greater than " 1055 "the number of available processors."}) 1056 end, 1057 1058 try 1059 OldVal = erlang:system_flag(scheduler_bind_type, default_bind), 1060 erlang:system_flag(scheduler_bind_type, OldVal) 1061 catch 1062 error:notsup -> 1063 throw({skipped, "Scheduler binding not supported."}); 1064 error:_ -> 1065 %% ?! 1066 ok 1067 end, 1068 1069 case erlang:system_info(logical_processors) of 1070 Count when is_integer(Count) -> 1071 ok; 1072 unknown -> 1073 throw({skipped, "Can't detect number of logical processors."}) 1074 end, 1075 1076 ok 1077 catch 1078 throw:{skip,_Reason}=Skip -> Skip 1079 end. 1080 1081sbt_test(Config, CpuTCmd, ClBt, Bt, LP) -> 1082 io:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]), 1083 LPS = integer_to_list(LP), 1084 Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS, 1085 {ok, Node} = start_node(Config, Cmd), 1086 Bt = rpc:call(Node, 1087 erlang, 1088 system_info, 1089 [scheduler_bind_type]), 1090 SB = rpc:call(Node, 1091 erlang, 1092 system_info, 1093 [scheduler_bindings]), 1094 io:format("scheduler bindings: ~p~n", [SB]), 1095 BS = case {Bt, erlang:system_info(logical_processors_available)} of 1096 {unbound, _} -> 0; 1097 {_, Int} when is_integer(Int) -> Int; 1098 {_, _} -> LP 1099 end, 1100 lists:foldl(fun (S, 0) -> 1101 unbound = S, 1102 0; 1103 (S, N) -> 1104 true = is_integer(S), 1105 N-1 1106 end, 1107 BS, 1108 tuple_to_list(SB)), 1109 stop_node(Node), 1110 ok. 1111 1112scheduler_threads(Config) when is_list(Config) -> 1113 {Sched, SchedOnln, _} = get_sstate(Config, ""), 1114 %% Configure half the number of both the scheduler threads and 1115 %% the scheduler threads online. 1116 {HalfSched, HalfSchedOnln} = {lists:max([1,Sched div 2]), 1117 lists:max([1,SchedOnln div 2])}, 1118 {HalfSched, HalfSchedOnln, _} = get_sstate(Config, "+SP 50:50"), 1119 %% Use +S to configure 4x the number of scheduler threads and 1120 %% 4x the number of scheduler threads online, but alter that 1121 %% setting using +SP to 50% scheduler threads and 25% scheduler 1122 %% threads online. The result should be 2x scheduler threads and 1123 %% 1x scheduler threads online. 1124 TwiceSched = Sched*2, 1125 FourSched = integer_to_list(Sched*4), 1126 FourSchedOnln = integer_to_list(SchedOnln*4), 1127 CombinedCmd1 = "+S "++FourSched++":"++FourSchedOnln++" +SP50:25", 1128 {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd1), 1129 %% Now do the same test but with the +S and +SP options in the 1130 %% opposite order, since order shouldn't matter. 1131 CombinedCmd2 = "+SP50:25 +S "++FourSched++":"++FourSchedOnln, 1132 {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd2), 1133 %% Apply two +SP options to make sure the second overrides the first 1134 TwoCmd = "+SP 25:25 +SP 100:100", 1135 {Sched, SchedOnln, _} = get_sstate(Config, TwoCmd), 1136 %% Configure 50% of scheduler threads online only 1137 {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"), 1138 %% Configure 2x scheduler threads only 1139 {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"), 1140 1141 LProc = erlang:system_info(logical_processors), 1142 LProcAvail = erlang:system_info(logical_processors_available), 1143 Quota = erlang:system_info(cpu_quota), 1144 1145 if 1146 not is_integer(LProc); not is_integer(LProcAvail) -> 1147 {comment, "Skipped reset amount of schedulers test, and reduced " 1148 "amount of schedulers test due to too unknown amount of " 1149 "logical processors"}; 1150 is_integer(LProc); is_integer(LProcAvail) -> 1151 ExpectedOnln = st_expected_onln(LProcAvail, Quota), 1152 1153 st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln), 1154 1155 if 1156 LProc =:= 1; LProcAvail =:= 1 -> 1157 {comment, "Skipped reduced amount of schedulers test due " 1158 "to too few logical processors"}; 1159 LProc > 1, LProcAvail > 1 -> 1160 st_reduced(Config, LProc, ExpectedOnln) 1161 end 1162 end. 1163 1164st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln) -> 1165 %% Test resetting # of schedulers. 1166 ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", 1167 {LProc, ExpectedOnln, _} = get_sstate(Config, ResetCmd), 1168 ok. 1169 1170st_reduced(Config, LProc, ExpectedOnln) -> 1171 %% Test negative +S settings 1172 SchedMinus1 = LProc-1, 1173 SchedOnlnMinus1 = ExpectedOnln-1, 1174 {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), 1175 {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), 1176 {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"), 1177 ok. 1178 1179st_expected_onln(LProcAvail, unknown) -> LProcAvail; 1180st_expected_onln(LProcAvail, Quota) -> min(LProcAvail, Quota). 1181 1182dirty_scheduler_threads(Config) when is_list(Config) -> 1183 case erlang:system_info(dirty_cpu_schedulers) of 1184 0 -> {skipped, "No dirty scheduler support"}; 1185 _ -> dirty_scheduler_threads_test(Config) 1186 end. 1187 1188dirty_scheduler_threads_test(Config) -> 1189 {Sched, SchedOnln, _} = get_dsstate(Config, ""), 1190 {HalfSched, HalfSchedOnln} = {lists:max([1,Sched div 2]), 1191 lists:max([1,SchedOnln div 2])}, 1192 Cmd1 = "+SDcpu "++integer_to_list(HalfSched)++":"++ 1193 integer_to_list(HalfSchedOnln), 1194 {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, Cmd1), 1195 {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, "+SDPcpu 50:50"), 1196 IOSched = 20, 1197 {_, _, IOSched} = get_dsstate(Config, "+SDio "++integer_to_list(IOSched)), 1198 {ok, Node} = start_node(Config, ""), 1199 [ok] = mcall(Node, [fun() -> dirty_schedulers_online_test() end]), 1200 ok. 1201 1202dirty_schedulers_online_test() -> 1203 dirty_schedulers_online_smp_test(erlang:system_info(schedulers_online)). 1204dirty_schedulers_online_smp_test(SchedOnln) when SchedOnln < 4 -> ok; 1205dirty_schedulers_online_smp_test(SchedOnln) -> 1206 receive after 500 -> ok end, 1207 DirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), 1208 SchedOnln = DirtyCPUSchedOnln, 1209 HalfSchedOnln = SchedOnln div 2, 1210 SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), 1211 HalfDirtyCPUSchedOnln = DirtyCPUSchedOnln div 2, 1212 HalfDirtyCPUSchedOnln = erlang:system_flag(schedulers_online, SchedOnln), 1213 DirtyCPUSchedOnln = erlang:system_flag(dirty_cpu_schedulers_online, 1214 HalfDirtyCPUSchedOnln), 1215 receive after 500 -> ok end, 1216 HalfDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), 1217 QrtrDirtyCPUSchedOnln = HalfDirtyCPUSchedOnln div 2, 1218 SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), 1219 receive after 500 -> ok end, 1220 QrtrDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), 1221 ok. 1222 1223get_sstate(Config, Cmd) -> 1224 {ok, Node} = start_node(Config, Cmd), 1225 [SState] = mcall(Node, [fun () -> 1226 erlang:system_info(schedulers_state) 1227 end]), 1228 stop_node(Node), 1229 SState. 1230 1231get_dsstate(Config, Cmd) -> 1232 {ok, Node} = start_node(Config, Cmd), 1233 [DSCPU] = mcall(Node, [fun () -> 1234 erlang:system_info(dirty_cpu_schedulers) 1235 end]), 1236 [DSCPUOnln] = mcall(Node, [fun () -> 1237 erlang:system_info(dirty_cpu_schedulers_online) 1238 end]), 1239 [DSIO] = mcall(Node, [fun () -> 1240 erlang:system_info(dirty_io_schedulers) 1241 end]), 1242 stop_node(Node), 1243 {DSCPU, DSCPUOnln, DSIO}. 1244 1245scheduler_suspend_basic(Config) when is_list(Config) -> 1246 case erlang:system_info(multi_scheduling) of 1247 disabled -> 1248 {skip, "Nothing to test"}; 1249 _ -> 1250 Onln = erlang:system_info(schedulers_online), 1251 try 1252 scheduler_suspend_basic_test() 1253 after 1254 erlang:system_flag(schedulers_online, Onln) 1255 end 1256 end. 1257 1258scheduler_suspend_basic_test() -> 1259 %% The receives after setting scheduler states are there 1260 %% since the operation is not fully synchronous. For example, 1261 %% we do not wait for dirty cpu schedulers online to complete 1262 %% before returning from erlang:system_flag(schedulers_online, _). 1263 1264 erlang:system_flag(schedulers_online, 1265 erlang:system_info(schedulers)), 1266 try 1267 erlang:system_flag(dirty_cpu_schedulers_online, 1268 erlang:system_info(dirty_cpu_schedulers)), 1269 receive after 500 -> ok end 1270 catch 1271 _ : _ -> 1272 ok 1273 end, 1274 1275 S0 = sched_state(), 1276 io:format("~p~n", [S0]), 1277 {{normal,NTot0,NOnln0,NAct0}, 1278 {dirty_cpu,DCTot0,DCOnln0,DCAct0}, 1279 {dirty_io,DITot0,DIOnln0,DIAct0}} = S0, 1280 enabled = erlang:system_info(multi_scheduling), 1281 1282 DCOne = case DCTot0 of 1283 0 -> 0; 1284 _ -> 1 1285 end, 1286 1287 blocked_normal = erlang:system_flag(multi_scheduling, block_normal), 1288 blocked_normal = erlang:system_info(multi_scheduling), 1289 {{normal,NTot0,NOnln0,1}, 1290 {dirty_cpu,DCTot0,DCOnln0,DCAct0}, 1291 {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), 1292 1293 NOnln0 = erlang:system_flag(schedulers_online, 1), 1294 receive after 500 -> ok end, 1295 {{normal,NTot0,1,1}, 1296 {dirty_cpu,DCTot0,DCOne,DCOne}, 1297 {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), 1298 1299 1 = erlang:system_flag(schedulers_online, NOnln0), 1300 receive after 500 -> ok end, 1301 {{normal,NTot0,NOnln0,1}, 1302 {dirty_cpu,DCTot0,DCOnln0,DCAct0}, 1303 {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), 1304 1305 blocked = erlang:system_flag(multi_scheduling, block), 1306 blocked = erlang:system_info(multi_scheduling), 1307 receive after 500 -> ok end, 1308 {{normal,NTot0,NOnln0,1}, 1309 {dirty_cpu,DCTot0,DCOnln0,0}, 1310 {dirty_io,DITot0,DIOnln0,0}} = sched_state(), 1311 1312 NOnln0 = erlang:system_flag(schedulers_online, 1), 1313 receive after 500 -> ok end, 1314 {{normal,NTot0,1,1}, 1315 {dirty_cpu,DCTot0,DCOne,0}, 1316 {dirty_io,DITot0,DIOnln0,0}} = sched_state(), 1317 1318 1 = erlang:system_flag(schedulers_online, NOnln0), 1319 receive after 500 -> ok end, 1320 {{normal,NTot0,NOnln0,1}, 1321 {dirty_cpu,DCTot0,DCOnln0,0}, 1322 {dirty_io,DITot0,DIOnln0,0}} = sched_state(), 1323 1324 blocked = erlang:system_flag(multi_scheduling, unblock_normal), 1325 blocked = erlang:system_info(multi_scheduling), 1326 {{normal,NTot0,NOnln0,1}, 1327 {dirty_cpu,DCTot0,DCOnln0,0}, 1328 {dirty_io,DITot0,DIOnln0,0}} = sched_state(), 1329 1330 enabled = erlang:system_flag(multi_scheduling, unblock), 1331 enabled = erlang:system_info(multi_scheduling), 1332 receive after 500 -> ok end, 1333 {{normal,NTot0,NOnln0,NAct0}, 1334 {dirty_cpu,DCTot0,DCOnln0,DCAct0}, 1335 {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), 1336 1337 NOnln0 = erlang:system_flag(schedulers_online, 1), 1338 receive after 500 -> ok end, 1339 {{normal,NTot0,1,1}, 1340 {dirty_cpu,DCTot0,DCOne,DCOne}, 1341 {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), 1342 1343 1 = erlang:system_flag(schedulers_online, NOnln0), 1344 receive after 500 -> ok end, 1345 {{normal,NTot0,NOnln0,NAct0}, 1346 {dirty_cpu,DCTot0,DCOnln0,DCAct0}, 1347 {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), 1348 1349 ok. 1350 1351 1352scheduler_suspend(Config) when is_list(Config) -> 1353 ct:timetrap({minutes, 5}), 1354 lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, 1355 [64, 32, 16, default]), 1356 ok. 1357scheduler_suspend_test(Config, Schedulers) -> 1358 Cmd = case Schedulers of 1359 default -> 1360 ""; 1361 _ -> 1362 S = integer_to_list(Schedulers), 1363 "+S"++S++":"++S 1364 end, 1365 {ok, Node} = start_node(Config, Cmd), 1366 [SState] = mcall(Node, [fun () -> 1367 erlang:system_info(schedulers_state) 1368 end]), 1369 1370 io:format("SState=~p~n", [SState]), 1371 {Sched, SchedOnln, _SchedAvail} = SState, 1372 true = is_integer(Sched), 1373 [ok] = mcall(Node, [fun () -> sst0_loop(300) end]), 1374 [ok] = mcall(Node, [fun () -> sst1_loop(300) end]), 1375 [ok] = mcall(Node, [fun () -> sst2_loop(300) end]), 1376 [ok] = mcall(Node, [fun () -> sst4_loop(300) end]), 1377 [ok] = mcall(Node, [fun () -> sst5_loop(300) end]), 1378 [ok, ok, ok, ok, 1379 ok, ok, ok] = mcall(Node, 1380 [fun () -> sst0_loop(200) end, 1381 fun () -> sst1_loop(200) end, 1382 fun () -> sst2_loop(200) end, 1383 fun () -> sst2_loop(200) end, 1384 fun () -> sst3_loop(Sched, 200) end, 1385 fun () -> sst4_loop(200) end, 1386 fun () -> sst5_loop(200) end]), 1387 [SState] = mcall(Node, [fun () -> 1388 case Sched == SchedOnln of 1389 false -> 1390 Sched = erlang:system_flag( 1391 schedulers_online, 1392 SchedOnln); 1393 true -> 1394 ok 1395 end, 1396 until(fun () -> 1397 {_A, B, C} = erlang:system_info( 1398 schedulers_state), 1399 B == C 1400 end, 1401 erlang:monotonic_time() 1402 + erlang:convert_time_unit(1, 1403 seconds, 1404 native)), 1405 erlang:system_info(schedulers_state) 1406 end]), 1407 stop_node(Node), 1408 ok. 1409 1410until(Pred, MaxTime) -> 1411 case Pred() of 1412 true -> 1413 true; 1414 false -> 1415 case erlang:monotonic_time() > MaxTime of 1416 true -> 1417 false; 1418 false -> 1419 receive after 100 -> ok end, 1420 until(Pred, MaxTime) 1421 end 1422 end. 1423 1424sst0_loop(0) -> 1425 ok; 1426sst0_loop(N) -> 1427 erlang:system_flag(multi_scheduling, block), 1428 erlang:system_flag(multi_scheduling, unblock), 1429 erlang:yield(), 1430 sst0_loop(N-1). 1431 1432sst1_loop(0) -> 1433 ok; 1434sst1_loop(N) -> 1435 erlang:system_flag(multi_scheduling, block), 1436 erlang:system_flag(multi_scheduling, unblock), 1437 sst1_loop(N-1). 1438 1439sst2_loop(0) -> 1440 ok; 1441sst2_loop(N) -> 1442 erlang:system_flag(multi_scheduling, block), 1443 erlang:system_flag(multi_scheduling, block), 1444 erlang:system_flag(multi_scheduling, block), 1445 erlang:system_flag(multi_scheduling, unblock), 1446 erlang:system_flag(multi_scheduling, unblock), 1447 erlang:system_flag(multi_scheduling, unblock), 1448 sst2_loop(N-1). 1449 1450sst3_loop(S, N) -> 1451 case erlang:system_info(dirty_cpu_schedulers) of 1452 0 -> sst3_loop_normal_schedulers_only(S, N); 1453 DS -> sst3_loop_with_dirty_schedulers(S, DS, N) 1454 end. 1455 1456sst3_loop_normal_schedulers_only(_S, 0) -> 1457 ok; 1458sst3_loop_normal_schedulers_only(S, N) -> 1459 erlang:system_flag(schedulers_online, (S div 2)+1), 1460 erlang:system_flag(schedulers_online, 1), 1461 erlang:system_flag(schedulers_online, (S div 2)+1), 1462 erlang:system_flag(schedulers_online, S), 1463 erlang:system_flag(schedulers_online, 1), 1464 erlang:system_flag(schedulers_online, S), 1465 sst3_loop_normal_schedulers_only(S, N-1). 1466 1467sst3_loop_with_dirty_schedulers(_S, _DS, 0) -> 1468 ok; 1469sst3_loop_with_dirty_schedulers(S, DS, N) -> 1470 erlang:system_flag(schedulers_online, (S div 2)+1), 1471 erlang:system_flag(dirty_cpu_schedulers_online, (DS div 2)+1), 1472 erlang:system_flag(schedulers_online, 1), 1473 erlang:system_flag(schedulers_online, (S div 2)+1), 1474 erlang:system_flag(dirty_cpu_schedulers_online, 1), 1475 erlang:system_flag(schedulers_online, S), 1476 erlang:system_flag(dirty_cpu_schedulers_online, DS), 1477 erlang:system_flag(schedulers_online, 1), 1478 erlang:system_flag(schedulers_online, S), 1479 erlang:system_flag(dirty_cpu_schedulers_online, DS), 1480 sst3_loop_with_dirty_schedulers(S, DS, N-1). 1481 1482sst4_loop(0) -> 1483 ok; 1484sst4_loop(N) -> 1485 erlang:system_flag(multi_scheduling, block_normal), 1486 erlang:system_flag(multi_scheduling, unblock_normal), 1487 sst4_loop(N-1). 1488 1489sst5_loop(0) -> 1490 ok; 1491sst5_loop(N) -> 1492 erlang:system_flag(multi_scheduling, block_normal), 1493 erlang:system_flag(multi_scheduling, unblock_normal), 1494 sst5_loop(N-1). 1495 1496poll_threads(Config) when is_list(Config) -> 1497 {Conc, PollType, KP} = get_ioconfig(Config), 1498 {Sched, SchedOnln, _} = get_sstate(Config, ""), 1499 1500 [1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), 1501 [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), 1502 [1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), 1503 1504 if 1505 Conc -> 1506 1507 [5] = get_ionum(Config,"+IOt 5 +IOp 1"), 1508 [3, 2] = get_ionum(Config,"+IOt 5 +IOp 2"), 1509 [2, 2, 2, 2, 2] = get_ionum(Config,"+IOt 10 +IOPp 50"), 1510 1511 [2] = get_ionum(Config, "+S 2 +IOPt 100"), 1512 [4] = get_ionum(Config, "+S 4 +IOPt 100"), 1513 [4] = get_ionum(Config, "+S 4:2 +IOPt 100"), 1514 [4, 4] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"), 1515 1516 fail = get_ionum(Config, "+IOt 1 +IOp 2"), 1517 1518 ok; 1519 not Conc -> 1520 1521 [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 1"), 1522 [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 2"), 1523 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 10 +IOPp 50"), 1524 1525 [1, 1] = get_ionum(Config, "+S 2 +IOPt 100"), 1526 [1, 1, 1, 1] = get_ionum(Config, "+S 4 +IOPt 100"), 1527 [1, 1, 1, 1] = get_ionum(Config, "+S 4:2 +IOPt 100"), 1528 [1, 1, 1, 1, 1, 1, 1, 1] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"), 1529 1530 [1] = get_ionum(Config, "+IOt 1 +IOp 2"), 1531 1532 ok 1533 end, 1534 1535 fail = get_ionum(Config, "+IOt 1 +IOPp 101"), 1536 fail = get_ionum(Config, "+IOt 0"), 1537 fail = get_ionum(Config, "+IOPt 101"), 1538 1539 ok. 1540 1541get_ioconfig(Config) -> 1542 [PS | _] = get_iostate(Config, ""), 1543 {proplists:get_value(concurrent_updates, PS), 1544 proplists:get_value(primary, PS), 1545 proplists:get_value(kernel_poll, PS)}. 1546 1547get_ionum(Config, Cmd) -> 1548 case get_iostate(Config, Cmd) of 1549 fail -> fail; 1550 PSs -> 1551 lists:reverse( 1552 lists:sort( 1553 [proplists:get_value(poll_threads, PS) || PS <- PSs])) 1554 end. 1555 1556get_iostate(Config, Cmd)-> 1557 case start_node(Config, Cmd) of 1558 {ok, Node} -> 1559 [IOStates] = mcall(Node,[fun () -> 1560 erlang:system_info(check_io) 1561 end]), 1562 IO = [IOState || IOState <- IOStates, 1563 proplists:get_value(fallback, IOState) == false, 1564 proplists:get_value(poll_threads, IOState) /= 0], 1565 stop_node(Node), 1566 IO; 1567 {error,timeout} -> 1568 fail 1569 end. 1570 1571reader_groups(Config) when is_list(Config) -> 1572 %% White box testing. These results are correct, but other results 1573 %% could be too... 1574 1575 %% The actual tilepro64 topology 1576 CPUT0 = [{processor,[{node,[{core,{logical,0}}, 1577 {core,{logical,1}}, 1578 {core,{logical,2}}, 1579 {core,{logical,8}}, 1580 {core,{logical,9}}, 1581 {core,{logical,10}}, 1582 {core,{logical,11}}, 1583 {core,{logical,16}}, 1584 {core,{logical,17}}, 1585 {core,{logical,18}}, 1586 {core,{logical,19}}, 1587 {core,{logical,24}}, 1588 {core,{logical,25}}, 1589 {core,{logical,27}}, 1590 {core,{logical,29}}]}, 1591 {node,[{core,{logical,3}}, 1592 {core,{logical,4}}, 1593 {core,{logical,5}}, 1594 {core,{logical,6}}, 1595 {core,{logical,7}}, 1596 {core,{logical,12}}, 1597 {core,{logical,13}}, 1598 {core,{logical,14}}, 1599 {core,{logical,15}}, 1600 {core,{logical,20}}, 1601 {core,{logical,21}}, 1602 {core,{logical,22}}, 1603 {core,{logical,23}}, 1604 {core,{logical,28}}, 1605 {core,{logical,30}}]}, 1606 {node,[{core,{logical,31}}, 1607 {core,{logical,36}}, 1608 {core,{logical,37}}, 1609 {core,{logical,38}}, 1610 {core,{logical,44}}, 1611 {core,{logical,45}}, 1612 {core,{logical,46}}, 1613 {core,{logical,47}}, 1614 {core,{logical,51}}, 1615 {core,{logical,52}}, 1616 {core,{logical,53}}, 1617 {core,{logical,54}}, 1618 {core,{logical,55}}, 1619 {core,{logical,60}}, 1620 {core,{logical,61}}]}, 1621 {node,[{core,{logical,26}}, 1622 {core,{logical,32}}, 1623 {core,{logical,33}}, 1624 {core,{logical,34}}, 1625 {core,{logical,35}}, 1626 {core,{logical,39}}, 1627 {core,{logical,40}}, 1628 {core,{logical,41}}, 1629 {core,{logical,42}}, 1630 {core,{logical,43}}, 1631 {core,{logical,48}}, 1632 {core,{logical,49}}, 1633 {core,{logical,50}}, 1634 {core,{logical,58}}]}]}], 1635 1636 [{0,1},{1,1},{2,1},{3,3},{4,3},{5,3},{6,3},{7,3},{8,1},{9,1},{10,1}, 1637 {11,1},{12,3},{13,3},{14,4},{15,4},{16,2},{17,2},{18,2},{19,2}, 1638 {20,4},{21,4},{22,4},{23,4},{24,2},{25,2},{26,7},{27,2},{28,4}, 1639 {29,2},{30,4},{31,5},{32,7},{33,7},{34,7},{35,7},{36,5},{37,5}, 1640 {38,5},{39,7},{40,7},{41,8},{42,8},{43,8},{44,5},{45,5},{46,5}, 1641 {47,6},{48,8},{49,8},{50,8},{51,6},{52,6},{53,6},{54,6},{55,6}, 1642 {58,8},{60,6},{61,6}] 1643 = reader_groups_map(CPUT0, 8), 1644 1645 CPUT1 = [n([p([c([t(l(0)),t(l(1)),t(l(2)),t(l(3))]), 1646 c([t(l(4)),t(l(5)),t(l(6)),t(l(7))]), 1647 c([t(l(8)),t(l(9)),t(l(10)),t(l(11))]), 1648 c([t(l(12)),t(l(13)),t(l(14)),t(l(15))])]), 1649 p([c([t(l(16)),t(l(17)),t(l(18)),t(l(19))]), 1650 c([t(l(20)),t(l(21)),t(l(22)),t(l(23))]), 1651 c([t(l(24)),t(l(25)),t(l(26)),t(l(27))]), 1652 c([t(l(28)),t(l(29)),t(l(30)),t(l(31))])])]), 1653 n([p([c([t(l(32)),t(l(33)),t(l(34)),t(l(35))]), 1654 c([t(l(36)),t(l(37)),t(l(38)),t(l(39))]), 1655 c([t(l(40)),t(l(41)),t(l(42)),t(l(43))]), 1656 c([t(l(44)),t(l(45)),t(l(46)),t(l(47))])]), 1657 p([c([t(l(48)),t(l(49)),t(l(50)),t(l(51))]), 1658 c([t(l(52)),t(l(53)),t(l(54)),t(l(55))]), 1659 c([t(l(56)),t(l(57)),t(l(58)),t(l(59))]), 1660 c([t(l(60)),t(l(61)),t(l(62)),t(l(63))])])]), 1661 n([p([c([t(l(64)),t(l(65)),t(l(66)),t(l(67))]), 1662 c([t(l(68)),t(l(69)),t(l(70)),t(l(71))]), 1663 c([t(l(72)),t(l(73)),t(l(74)),t(l(75))]), 1664 c([t(l(76)),t(l(77)),t(l(78)),t(l(79))])]), 1665 p([c([t(l(80)),t(l(81)),t(l(82)),t(l(83))]), 1666 c([t(l(84)),t(l(85)),t(l(86)),t(l(87))]), 1667 c([t(l(88)),t(l(89)),t(l(90)),t(l(91))]), 1668 c([t(l(92)),t(l(93)),t(l(94)),t(l(95))])])]), 1669 n([p([c([t(l(96)),t(l(97)),t(l(98)),t(l(99))]), 1670 c([t(l(100)),t(l(101)),t(l(102)),t(l(103))]), 1671 c([t(l(104)),t(l(105)),t(l(106)),t(l(107))]), 1672 c([t(l(108)),t(l(109)),t(l(110)),t(l(111))])]), 1673 p([c([t(l(112)),t(l(113)),t(l(114)),t(l(115))]), 1674 c([t(l(116)),t(l(117)),t(l(118)),t(l(119))]), 1675 c([t(l(120)),t(l(121)),t(l(122)),t(l(123))]), 1676 c([t(l(124)),t(l(125)),t(l(126)),t(l(127))])])])], 1677 1678 [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3}, 1679 {10,3},{11,3},{12,4},{13,4},{14,4},{15,4},{16,5},{17,5},{18,5}, 1680 {19,5},{20,6},{21,6},{22,6},{23,6},{24,7},{25,7},{26,7},{27,7}, 1681 {28,8},{29,8},{30,8},{31,8},{32,9},{33,9},{34,9},{35,9},{36,10}, 1682 {37,10},{38,10},{39,10},{40,11},{41,11},{42,11},{43,11},{44,12}, 1683 {45,12},{46,12},{47,12},{48,13},{49,13},{50,13},{51,13},{52,14}, 1684 {53,14},{54,14},{55,14},{56,15},{57,15},{58,15},{59,15},{60,16}, 1685 {61,16},{62,16},{63,16},{64,17},{65,17},{66,17},{67,17},{68,18}, 1686 {69,18},{70,18},{71,18},{72,19},{73,19},{74,19},{75,19},{76,20}, 1687 {77,20},{78,20},{79,20},{80,21},{81,21},{82,21},{83,21},{84,22}, 1688 {85,22},{86,22},{87,22},{88,23},{89,23},{90,23},{91,23},{92,24}, 1689 {93,24},{94,24},{95,24},{96,25},{97,25},{98,25},{99,25},{100,26}, 1690 {101,26},{102,26},{103,26},{104,27},{105,27},{106,27},{107,27}, 1691 {108,28},{109,28},{110,28},{111,28},{112,29},{113,29},{114,29}, 1692 {115,29},{116,30},{117,30},{118,30},{119,30},{120,31},{121,31}, 1693 {122,31},{123,31},{124,32},{125,32},{126,32},{127,32}] 1694 = reader_groups_map(CPUT1, 128), 1695 1696 [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1}, 1697 {11,1},{12,1},{13,1},{14,1},{15,1},{16,1},{17,1},{18,1},{19,1}, 1698 {20,1},{21,1},{22,1},{23,1},{24,1},{25,1},{26,1},{27,1},{28,1}, 1699 {29,1},{30,1},{31,1},{32,1},{33,1},{34,1},{35,1},{36,1},{37,1}, 1700 {38,1},{39,1},{40,1},{41,1},{42,1},{43,1},{44,1},{45,1},{46,1}, 1701 {47,1},{48,1},{49,1},{50,1},{51,1},{52,1},{53,1},{54,1},{55,1}, 1702 {56,1},{57,1},{58,1},{59,1},{60,1},{61,1},{62,1},{63,1},{64,2}, 1703 {65,2},{66,2},{67,2},{68,2},{69,2},{70,2},{71,2},{72,2},{73,2}, 1704 {74,2},{75,2},{76,2},{77,2},{78,2},{79,2},{80,2},{81,2},{82,2}, 1705 {83,2},{84,2},{85,2},{86,2},{87,2},{88,2},{89,2},{90,2},{91,2}, 1706 {92,2},{93,2},{94,2},{95,2},{96,2},{97,2},{98,2},{99,2},{100,2}, 1707 {101,2},{102,2},{103,2},{104,2},{105,2},{106,2},{107,2},{108,2}, 1708 {109,2},{110,2},{111,2},{112,2},{113,2},{114,2},{115,2},{116,2}, 1709 {117,2},{118,2},{119,2},{120,2},{121,2},{122,2},{123,2},{124,2}, 1710 {125,2},{126,2},{127,2}] 1711 = reader_groups_map(CPUT1, 2), 1712 1713 [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},{10,3}, 1714 {11,3},{12,3},{13,3},{14,3},{15,3},{16,4},{17,4},{18,4},{19,4}, 1715 {20,4},{21,4},{22,4},{23,4},{24,5},{25,5},{26,5},{27,5},{28,5}, 1716 {29,5},{30,5},{31,5},{32,6},{33,6},{34,6},{35,6},{36,6},{37,6}, 1717 {38,6},{39,6},{40,7},{41,7},{42,7},{43,7},{44,7},{45,7},{46,7}, 1718 {47,7},{48,8},{49,8},{50,8},{51,8},{52,8},{53,8},{54,8},{55,8}, 1719 {56,9},{57,9},{58,9},{59,9},{60,9},{61,9},{62,9},{63,9},{64,10}, 1720 {65,10},{66,10},{67,10},{68,10},{69,10},{70,10},{71,10},{72,11}, 1721 {73,11},{74,11},{75,11},{76,11},{77,11},{78,11},{79,11},{80,12}, 1722 {81,12},{82,12},{83,12},{84,12},{85,12},{86,12},{87,12},{88,13}, 1723 {89,13},{90,13},{91,13},{92,13},{93,13},{94,13},{95,13},{96,14}, 1724 {97,14},{98,14},{99,14},{100,14},{101,14},{102,14},{103,14}, 1725 {104,15},{105,15},{106,15},{107,15},{108,15},{109,15},{110,15}, 1726 {111,15},{112,16},{113,16},{114,16},{115,16},{116,16},{117,16}, 1727 {118,16},{119,16},{120,17},{121,17},{122,17},{123,17},{124,17}, 1728 {125,17},{126,17},{127,17}] 1729 = reader_groups_map(CPUT1, 17), 1730 1731 [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1}, 1732 {11,1},{12,1},{13,1},{14,1},{15,1},{16,2},{17,2},{18,2},{19,2}, 1733 {20,2},{21,2},{22,2},{23,2},{24,2},{25,2},{26,2},{27,2},{28,2}, 1734 {29,2},{30,2},{31,2},{32,3},{33,3},{34,3},{35,3},{36,3},{37,3}, 1735 {38,3},{39,3},{40,3},{41,3},{42,3},{43,3},{44,3},{45,3},{46,3}, 1736 {47,3},{48,4},{49,4},{50,4},{51,4},{52,4},{53,4},{54,4},{55,4}, 1737 {56,4},{57,4},{58,4},{59,4},{60,4},{61,4},{62,4},{63,4},{64,5}, 1738 {65,5},{66,5},{67,5},{68,5},{69,5},{70,5},{71,5},{72,5},{73,5}, 1739 {74,5},{75,5},{76,5},{77,5},{78,5},{79,5},{80,6},{81,6},{82,6}, 1740 {83,6},{84,6},{85,6},{86,6},{87,6},{88,6},{89,6},{90,6},{91,6}, 1741 {92,6},{93,6},{94,6},{95,6},{96,7},{97,7},{98,7},{99,7},{100,7}, 1742 {101,7},{102,7},{103,7},{104,7},{105,7},{106,7},{107,7},{108,7}, 1743 {109,7},{110,7},{111,7},{112,7},{113,7},{114,7},{115,7},{116,7}, 1744 {117,7},{118,7},{119,7},{120,7},{121,7},{122,7},{123,7},{124,7}, 1745 {125,7},{126,7},{127,7}] 1746 = reader_groups_map(CPUT1, 7), 1747 1748 CPUT2 = [p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))]), 1749 p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]), 1750 p([t(l(10))]), 1751 p([c(l(11)),c(l(12)),c(l(13))]), 1752 p([c(l(14)),c(l(15))])], 1753 1754 [{0,1},{1,1},{2,1},{3,1},{4,1}, 1755 {5,2},{6,2},{7,2},{8,2},{9,2}, 1756 {10,3}, 1757 {11,4},{12,4},{13,4}, 1758 {14,5},{15,5}] = reader_groups_map(CPUT2, 5), 1759 1760 1761 [{0,1},{1,1},{2,2},{3,2},{4,2}, 1762 {5,3},{6,3},{7,3},{8,3},{9,3}, 1763 {10,4}, 1764 {11,5},{12,5},{13,5}, 1765 {14,6},{15,6}] = reader_groups_map(CPUT2, 6), 1766 1767 [{0,1},{1,1},{2,2},{3,2},{4,2}, 1768 {5,3},{6,3},{7,3},{8,3},{9,3}, 1769 {10,4}, 1770 {11,5},{12,6},{13,6}, 1771 {14,7},{15,7}] = reader_groups_map(CPUT2, 7), 1772 1773 [{0,1},{1,1},{2,2},{3,2},{4,2}, 1774 {5,3},{6,3},{7,3},{8,3},{9,3}, 1775 {10,4}, 1776 {11,5},{12,6},{13,6}, 1777 {14,7},{15,8}] = reader_groups_map(CPUT2, 8), 1778 1779 [{0,1},{1,2},{2,2},{3,3},{4,3}, 1780 {5,4},{6,4},{7,4},{8,4},{9,4}, 1781 {10,5}, 1782 {11,6},{12,7},{13,7}, 1783 {14,8},{15,9}] = reader_groups_map(CPUT2, 9), 1784 1785 [{0,1},{1,2},{2,2},{3,3},{4,3}, 1786 {5,4},{6,4},{7,4},{8,4},{9,4}, 1787 {10,5}, 1788 {11,6},{12,7},{13,8}, 1789 {14,9},{15,10}] = reader_groups_map(CPUT2, 10), 1790 1791 [{0,1},{1,2},{2,3},{3,4},{4,4}, 1792 {5,5},{6,5},{7,5},{8,5},{9,5}, 1793 {10,6}, 1794 {11,7},{12,8},{13,9}, 1795 {14,10},{15,11}] = reader_groups_map(CPUT2, 11), 1796 1797 [{0,1},{1,2},{2,3},{3,4},{4,5}, 1798 {5,6},{6,6},{7,6},{8,6},{9,6}, 1799 {10,7}, 1800 {11,8},{12,9},{13,10}, 1801 {14,11},{15,12}] = reader_groups_map(CPUT2, 100), 1802 1803 CPUT3 = [p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]), 1804 p([t(l(10))]), 1805 p([c(l(11)),c(l(12)),c(l(13))]), 1806 p([c(l(14)),c(l(15))]), 1807 p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))])], 1808 1809 [{0,5},{1,5},{2,6},{3,6},{4,6}, 1810 {5,1},{6,1},{7,1},{8,1},{9,1}, 1811 {10,2},{11,3},{12,3},{13,3}, 1812 {14,4},{15,4}] = reader_groups_map(CPUT3, 6), 1813 1814 CPUT4 = [p([t(l(0)),t(l(1)),t(l(2)),t(l(3)),t(l(4))]), 1815 p([t(l(5))]), 1816 p([c(l(6)),c(l(7)),c(l(8))]), 1817 p([c(l(9)),c(l(10))]), 1818 p([c(l(11)),c(l(12)),c(l(13)),c(l(14)),c(l(15))])], 1819 1820 [{0,1},{1,1},{2,1},{3,1},{4,1}, 1821 {5,2}, 1822 {6,3},{7,3},{8,3}, 1823 {9,4},{10,4}, 1824 {11,5},{12,5},{13,6},{14,6},{15,6}] = reader_groups_map(CPUT4, 6), 1825 1826 [{0,1},{1,1},{2,1},{3,1},{4,1}, 1827 {5,2}, 1828 {6,3},{7,4},{8,4}, 1829 {9,5},{10,5}, 1830 {11,6},{12,6},{13,7},{14,7},{15,7}] = reader_groups_map(CPUT4, 7), 1831 1832 [{0,1},{65535,2}] = reader_groups_map([c(l(0)),c(l(65535))], 10), 1833 ok. 1834 1835 1836reader_groups_map(CPUT, Groups) -> 1837 Old = erlang:system_info({cpu_topology, defined}), 1838 erlang:system_flag(cpu_topology, CPUT), 1839 enable_internal_state(), 1840 Res = erts_debug:get_internal_state({reader_groups_map, Groups}), 1841 erlang:system_flag(cpu_topology, Old), 1842 lists:sort(Res). 1843 1844otp_16446(Config) when is_list(Config) -> 1845 ct:timetrap({minutes, 1}), 1846 1847 process_flag(priority, high), 1848 1849 DIO = erlang:system_info(dirty_io_schedulers), 1850 NoPrioProcs = 10*DIO, 1851 io:format("DIO = ~p~nNoPrioProcs = ~p~n", [DIO, NoPrioProcs]), 1852 1853 DirtyLoop = fun Loop(P, N) -> 1854 erts_debug:dirty_io(wait,1), 1855 receive {get, From} -> From ! {P, N} 1856 after 0 -> Loop(P,N+1) 1857 end 1858 end, 1859 1860 Spawn = fun SpawnLoop(_Prio, 0, Acc) -> 1861 Acc; 1862 SpawnLoop(Prio, N, Acc) -> 1863 Pid = spawn_opt(fun () -> DirtyLoop(Prio, 0) end, 1864 [link, {priority, Prio}]), 1865 SpawnLoop(Prio, N-1, [Pid|Acc]) 1866 end, 1867 1868 Ns = Spawn(normal, NoPrioProcs, []), 1869 Ls = Spawn(low, NoPrioProcs, []), 1870 1871 receive after 10000 -> ok end, 1872 1873 RequestInfo = fun (P) -> P ! {get, self()} end, 1874 lists:foreach(RequestInfo, Ns), 1875 lists:foreach(RequestInfo, Ls), 1876 1877 Collect = fun CollectFun(0, LLs, NLs) -> 1878 {LLs, NLs}; 1879 CollectFun(N, LLs, NLs) -> 1880 receive 1881 {low, Calls} -> 1882 CollectFun(N-1, LLs+Calls, NLs); 1883 {normal, Calls} -> 1884 CollectFun(N-1, LLs, NLs+Calls) 1885 end 1886 end, 1887 1888 {LLs, NLs} = Collect(2*NoPrioProcs, 0, 0), 1889 1890 %% expected ratio 0.125, but this is not especially exact... 1891 Ratio = LLs / NLs, 1892 1893 io:format("LLs = ~p~nNLs = ~p~nRatio = ~p~n", [LLs, NLs, Ratio]), 1894 1895 true = Ratio > 0.05, 1896 true = Ratio < 0.5, 1897 1898 WaitUntilDead = fun (P) -> 1899 case is_process_alive(P) of 1900 false -> 1901 ok; 1902 true -> 1903 unlink(P), 1904 exit(P, kill), 1905 false = is_process_alive(P) 1906 end 1907 end, 1908 1909 lists:foreach(WaitUntilDead, Ns), 1910 lists:foreach(WaitUntilDead, Ls), 1911 Comment = "low/normal ratio: " ++ erlang:float_to_list(Ratio,[{decimals,4}]), 1912 erlang:display(Comment), 1913 {comment, Comment}. 1914 1915simultaneously_change_schedulers_online(Config) when is_list(Config) -> 1916 SchedOnline = erlang:system_info(schedulers_online), 1917 Change = fun Change (0) -> 1918 ok; 1919 Change (N) -> 1920 %timer:sleep(rand:uniform(100)), 1921 erlang:system_flag(schedulers_online, 1922 rand:uniform(erlang:system_info(schedulers))), 1923 Change(N-1) 1924 end, 1925 PMs = lists:map(fun (_) -> 1926 spawn_monitor(fun () -> Change(10) end) 1927 end, lists:seq(1,2500)), 1928 lists:foreach(fun ({P, M}) -> 1929 receive 1930 {'DOWN', M, process, P, normal} -> 1931 ok 1932 end 1933 end, 1934 PMs), 1935 erlang:system_flag(schedulers_online, SchedOnline), 1936 ok. 1937 1938simultaneously_change_schedulers_online_with_exits(Config) when is_list(Config) -> 1939 SchedOnline = erlang:system_info(schedulers_online), 1940 Change = fun Change (0) -> 1941 exit(bye); 1942 Change (N) -> 1943 %timer:sleep(rand:uniform(100)), 1944 erlang:system_flag(schedulers_online, 1945 rand:uniform(erlang:system_info(schedulers))), 1946 Change(N-1) 1947 end, 1948 PMs = lists:map(fun (_) -> 1949 spawn_monitor(fun () -> Change(10) end) 1950 end, lists:seq(1,2500)), 1951 %% Kill every 10:th process... 1952 _ = lists:foldl(fun ({P, _M}, 0) -> 1953 exit(P, bye), 1954 10; 1955 (_PM, N) -> 1956 N-1 1957 end, 1958 10, 1959 PMs), 1960 lists:foreach(fun ({P, M}) -> 1961 receive 1962 {'DOWN', M, process, P, Reason} -> 1963 bye = Reason 1964 end 1965 end, 1966 PMs), 1967 erlang:system_flag(schedulers_online, SchedOnline), 1968 ok. 1969 1970 1971%% 1972%% Utils 1973%% 1974 1975sched_state() -> 1976 sched_state(erlang:system_info(all_schedulers_state), 1977 undefined, 1978 {dirty_cpu,0,0,0}, 1979 {dirty_io,0,0,0}). 1980 1981 1982sched_state([], N, DC, DI) -> 1983 try 1984 chk_basic(N), 1985 chk_basic(DC), 1986 chk_basic(DI), 1987 {N, DC, DI} 1988 catch 1989 _ : _ -> 1990 ct:fail({inconsisten_scheduler_state, {N, DC, DI}}) 1991 end; 1992sched_state([{normal, _, _, _} = S | Rest], _S, DC, DI) -> 1993 sched_state(Rest, S, DC, DI); 1994sched_state([{dirty_cpu, _, _, _} = DC | Rest], S, _DC, DI) -> 1995 sched_state(Rest, S, DC, DI); 1996sched_state([{dirty_io, _, _, _} = DI | Rest], S, DC, _DI) -> 1997 sched_state(Rest, S, DC, DI). 1998 1999chk_basic({_Type, Tot, Onln, Act}) -> 2000 true = Tot >= Onln, 2001 true = Onln >= Act. 2002 2003l(Id) -> 2004 {logical, Id}. 2005 2006t(X) -> 2007 {thread, X}. 2008 2009c(X) -> 2010 {core, X}. 2011 2012p(X) -> 2013 {processor, X}. 2014 2015n(X) -> 2016 {node, X}. 2017 2018mcall(Node, Funs) -> 2019 Parent = self(), 2020 Refs = lists:map(fun (Fun) -> 2021 Ref = make_ref(), 2022 Pid = spawn(Node, 2023 fun () -> 2024 Res = Fun(), 2025 unlink(Parent), 2026 Parent ! {Ref, Res} 2027 end), 2028 MRef = erlang:monitor(process, Pid), 2029 {Ref, MRef} 2030 end, Funs), 2031 lists:map(fun ({Ref, MRef}) -> 2032 receive 2033 {Ref, Res} -> 2034 receive 2035 {'DOWN',MRef,_,_,_} -> 2036 Res 2037 end; 2038 {'DOWN',MRef,_,_,Reason} -> 2039 Reason 2040 end 2041 end, Refs). 2042 2043erl_rel_flag_var() -> 2044 "ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS". 2045 2046clear_erl_rel_flags() -> 2047 EnvVar = erl_rel_flag_var(), 2048 case os:getenv(EnvVar) of 2049 false -> 2050 false; 2051 Value -> 2052 os:putenv(EnvVar, ""), 2053 Value 2054 end. 2055 2056restore_erl_rel_flags(false) -> 2057 ok; 2058restore_erl_rel_flags(OldValue) -> 2059 os:putenv(erl_rel_flag_var(), OldValue), 2060 ok. 2061 2062ok(too_slow, _Config) -> 2063 {comment, "Too slow system to do any actual testing..."}; 2064ok(_Res, Config) -> 2065 proplists:get_value(ok_res, Config). 2066 2067chk_result(too_slow, 2068 _LWorkers, 2069 _NWorkers, 2070 _HWorkers, 2071 _MWorkers, 2072 _LNShouldWork, 2073 _HShouldWork, 2074 _MShouldWork) -> 2075 ok; 2076chk_result([{low, L, Lmin, _Lmax}, 2077 {normal, N, Nmin, _Nmax}, 2078 {high, H, Hmin, _Hmax}, 2079 {max, M, Mmin, _Mmax}] = Res, 2080 LWorkers, 2081 NWorkers, 2082 HWorkers, 2083 MWorkers, 2084 LNShouldWork, 2085 HShouldWork, 2086 MShouldWork) -> 2087 io:format("~p~n", [Res]), 2088 Relax = relax_limits(), 2089 case {L, N} of 2090 {0, 0} -> 2091 false = LNShouldWork; 2092 _ -> 2093 {LminRatioLim, 2094 NminRatioLim, 2095 LNRatioLimMin, 2096 LNRatioLimMax} = case Relax of 2097 false -> {0.5, 0.5, 0.05, 0.25}; 2098 true -> {0.05, 0.05, 0.01, 0.4} 2099 end, 2100 Lavg = L/LWorkers, 2101 Navg = N/NWorkers, 2102 Ratio = Lavg/Navg, 2103 LminRatio = Lmin/Lavg, 2104 NminRatio = Nmin/Navg, 2105 io:format("low min ratio=~p~n" 2106 "normal min ratio=~p~n" 2107 "low avg=~p~n" 2108 "normal avg=~p~n" 2109 "low/normal ratio=~p~n", 2110 [LminRatio, NminRatio, Lavg, Navg, Ratio]), 2111 erlang:display({low_min_ratio, LminRatio}), 2112 erlang:display({normal_min_ratio, NminRatio}), 2113 erlang:display({low_avg, Lavg}), 2114 erlang:display({normal_avg, Navg}), 2115 erlang:display({low_normal_ratio, Ratio}), 2116 chk_lim(LminRatioLim, LminRatio, 1.0, low_min_ratio), 2117 chk_lim(NminRatioLim, NminRatio, 1.0, normal_min_ratio), 2118 chk_lim(LNRatioLimMin, Ratio, LNRatioLimMax, low_normal_ratio), 2119 true = LNShouldWork, 2120 ok 2121 end, 2122 case H of 2123 0 -> 2124 false = HShouldWork; 2125 _ -> 2126 HminRatioLim = case Relax of 2127 false -> 0.5; 2128 true -> 0.1 2129 end, 2130 Havg = H/HWorkers, 2131 HminRatio = Hmin/Havg, 2132 erlang:display({high_min_ratio, HminRatio}), 2133 chk_lim(HminRatioLim, HminRatio, 1.0, high_min_ratio), 2134 true = HShouldWork, 2135 ok 2136 end, 2137 case M of 2138 0 -> 2139 false = MShouldWork; 2140 _ -> 2141 MminRatioLim = case Relax of 2142 false -> 0.5; 2143 true -> 0.1 2144 end, 2145 Mavg = M/MWorkers, 2146 MminRatio = Mmin/Mavg, 2147 erlang:display({max_min_ratio, MminRatio}), 2148 chk_lim(MminRatioLim, MminRatio, 1.0, max_min_ratio), 2149 true = MShouldWork, 2150 ok 2151 end, 2152 ok. 2153 2154 2155 2156chk_lim(Min, V, Max, _What) when Min =< V, V =< Max -> 2157 ok; 2158chk_lim(_Min, V, _Max, What) -> 2159 ct:fail({bad, What, V}). 2160 2161snd(_Msg, []) -> 2162 []; 2163snd(Msg, [P|Ps]) -> 2164 P ! Msg, 2165 Ps. 2166 2167relax_limits() -> 2168 case strange_system_scale() of 2169 Scale when Scale > 1 -> 2170 io:format("Relaxing limits~n", []), 2171 true; 2172 _ -> 2173 false 2174 end. 2175 2176strange_system_scale() -> 2177 S0 = 1, 2178 S1 = case erlang:system_info(schedulers_online) 2179 > erlang:system_info(logical_processors) of 2180 true -> S0*2; 2181 false -> S0 2182 end, 2183 S2 = case erlang:system_info(debug_compiled) of 2184 true -> S1*10; 2185 false -> 2186 case erlang:system_info(lock_checking) of 2187 true -> S1*2; 2188 false -> S1 2189 end 2190 end, 2191 S3 = case lock_counting() of 2192 true -> S2*2; 2193 false -> S2 2194 end, 2195 S3. 2196 2197lock_counting() -> 2198 lock_counting(erlang:system_info(system_version)). 2199 2200lock_counting([]) -> 2201 false; 2202lock_counting([$[,$l,$o,$c,$k,$-,$c,$o,$u,$n,$t,$i,$n,$g,$],_]) -> 2203 true; 2204lock_counting([_C|Cs]) -> 2205 lock_counting(Cs). 2206 2207go_work([], [], [], []) -> 2208 []; 2209go_work(L, N, [], []) -> 2210 go_work(snd(go_work, L), snd(go_work, N), [], []); 2211go_work(L, N, H, []) -> 2212 go_work(L, N, snd(go_work, H), []); 2213go_work(L, N, H, M) -> 2214 go_work(L, N, H, snd(go_work, M)). 2215 2216stop_work([], [], [], []) -> 2217 []; 2218stop_work([], [], [], M) -> 2219 stop_work([], [], [], snd(stop_work, M)); 2220stop_work([], [], H, M) -> 2221 stop_work([], [], snd(stop_work, H), M); 2222stop_work(L, N, H, M) -> 2223 stop_work(snd(stop_work, L), snd(stop_work, N), H, M). 2224 2225wait_balance(N) when is_integer(N) -> 2226 case erlang:system_info(schedulers_active) of 2227 1 -> 2228 done; 2229 _ -> 2230 erts_debug:set_internal_state(available_internal_state,true), 2231 Start = erts_debug:get_internal_state(nbalance), 2232 End = (Start + N) band ((1 bsl (8*erlang:system_info(wordsize)))-1), 2233 wait_balance(Start, End), 2234 erts_debug:set_internal_state(available_internal_state,false) 2235 end. 2236 2237wait_balance(Start, End) -> 2238 X = erts_debug:get_internal_state(nbalance), 2239 case End =< X of 2240 true -> 2241 case Start =< End of 2242 true -> 2243 done; 2244 false -> 2245 case X < Start of 2246 true -> 2247 done; 2248 false -> 2249 receive after 250 -> ok end, 2250 wait_balance(Start, End) 2251 end 2252 end; 2253 false -> 2254 receive after 250 -> ok end, 2255 wait_balance(Start, End) 2256 end. 2257 2258wait_reds(RedsLimit, Timeout) -> 2259 Stop = erlang:start_timer(Timeout, self(), stop), 2260 statistics(reductions), 2261 wait_reds(0, RedsLimit, Stop). 2262 2263wait_reds(Reds, RedsLimit, Stop) when Reds < RedsLimit -> 2264 receive 2265 {timeout, Stop, stop} -> 2266 erlang:display(timeout), 2267 erlang:display({reduction_limit, RedsLimit}), 2268 erlang:display({reductions, Reds}), 2269 done 2270 after 10000 -> 2271 {_, NewReds} = statistics(reductions), 2272 wait_reds(NewReds+Reds, RedsLimit, Stop) 2273 end; 2274wait_reds(Reds, RedsLimit, Stop) when is_reference(Stop) -> 2275 erlang:cancel_timer(Stop), 2276 receive {timeout, Stop, stop} -> ok after 0 -> ok end, 2277 wait_reds(Reds, RedsLimit, false); 2278wait_reds(Reds, RedsLimit, _Stop) -> 2279 erlang:display({reduction_limit, RedsLimit}), 2280 erlang:display({reductions, Reds}), 2281 done. 2282 2283do_it(Tracer, Low, Normal, High, Max) -> 2284 do_it(Tracer, Low, Normal, High, Max, ?DEFAULT_TEST_REDS_PER_SCHED). 2285 2286do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) -> 2287 OldPrio = process_flag(priority, max), 2288 go_work(Low, Normal, High, Max), 2289 StartWait = erlang:monotonic_time(millisecond), 2290 %% Give the emulator a chance to balance the load... 2291 wait_balance(5), 2292 EndWait = erlang:monotonic_time(millisecond), 2293 BalanceWait = EndWait-StartWait, 2294 erlang:display({balance_wait, BalanceWait}), 2295 Timeout = (15 - 4)*60*1000 - BalanceWait, 2296 Res = case Timeout < 60*1000 of 2297 true -> 2298 stop_work(Low, Normal, High, Max), 2299 too_slow; 2300 false -> 2301 set_tracing(true, Tracer, normal, Normal), 2302 set_tracing(true, Tracer, low, Low), 2303 set_tracing(true, Tracer, high, High), 2304 set_tracing(true, Tracer, max, Max), 2305 wait_reds(RedsPerSchedLimit 2306 * erlang:system_info(schedulers_online), 2307 Timeout), 2308 set_tracing(false, Tracer, normal, Normal), 2309 set_tracing(false, Tracer, low, Low), 2310 set_tracing(false, Tracer, high, High), 2311 set_tracing(false, Tracer, max, Max), 2312 stop_work(Low, Normal, High, Max), 2313 get_trace_result(Tracer) 2314 end, 2315 process_flag(priority, OldPrio), 2316 Res. 2317 2318workers_exit([]) -> 2319 ok; 2320workers_exit([P|Ps]) when is_pid(P) -> 2321 Mon = erlang:monitor(process, P), 2322 unlink(P), 2323 exit(P, kill), 2324 workers_exit(Ps), 2325 receive {'DOWN', Mon, process, P, _} -> ok end, 2326 ok; 2327workers_exit([[]]) -> 2328 ok; 2329workers_exit([Ps|Pss]) -> 2330 workers_exit(Ps), 2331 workers_exit(Pss). 2332 2333do_work(PartTime) -> 2334 _ = id(lists:seq(1, 50)), 2335 receive stop_work -> receive after infinity -> ok end after 0 -> ok end, 2336 case PartTime of 2337 true -> receive after 1 -> ok end; 2338 false -> ok 2339 end, 2340 do_work(PartTime). 2341 2342id(I) -> I. 2343 2344workers(N, _Prio, _PartTime) when N =< 0 -> 2345 []; 2346workers(N, Prio, PartTime) -> 2347 Parent = self(), 2348 W = spawn_opt(fun () -> 2349 Parent ! {ready, self()}, 2350 receive 2351 go_work -> 2352 do_work(PartTime) 2353 end 2354 end, 2355 [{priority, Prio}, link]), 2356 Ws = workers(N-1, Prio, PartTime), 2357 receive {ready, W} -> ok end, 2358 [W|Ws]. 2359 2360workers(N, Prio) -> 2361 workers(N, Prio, false). 2362 2363part_time_workers(N, Prio) -> 2364 workers(N, Prio, true). 2365 2366tracer(Low, Normal, High, Max) -> 2367 receive 2368 {tracees, Prio, Tracees} -> 2369 save_tracees(Prio, Tracees), 2370 case Prio of 2371 low -> tracer(Tracees++Low, Normal, High, Max); 2372 normal -> tracer(Low, Tracees++Normal, High, Max); 2373 high -> tracer(Low, Normal, Tracees++High, Max); 2374 max -> tracer(Low, Normal, High, Tracees++Max) 2375 end; 2376 {get_result, Ref, Who} -> 2377 Delivered = erlang:trace_delivered(all), 2378 receive 2379 {trace_delivered, all, Delivered} -> 2380 ok 2381 end, 2382 {Lc, Nc, Hc, Mc} = read_trace(), 2383 GetMinMax 2384 = fun (Prio, Procs) -> 2385 LargeNum = 1 bsl 64, 2386 case lists:foldl(fun (P, {Mn, Mx} = MnMx) -> 2387 {Prio, C} = get(P), 2388 case C < Mn of 2389 true -> 2390 case C > Mx of 2391 true -> 2392 {C, C}; 2393 false -> 2394 {C, Mx} 2395 end; 2396 false -> 2397 case C > Mx of 2398 true -> {Mn, C}; 2399 false -> MnMx 2400 end 2401 end 2402 end, 2403 {LargeNum, 0}, 2404 Procs) of 2405 {LargeNum, 0} -> {0, 0}; 2406 Res -> Res 2407 end 2408 end, 2409 {Lmin, Lmax} = GetMinMax(low, Low), 2410 {Nmin, Nmax} = GetMinMax(normal, Normal), 2411 {Hmin, Hmax} = GetMinMax(high, High), 2412 {Mmin, Mmax} = GetMinMax(max, Max), 2413 Who ! {trace_result, Ref, [{low, Lc, Lmin, Lmax}, 2414 {normal, Nc, Nmin, Nmax}, 2415 {high, Hc, Hmin, Hmax}, 2416 {max, Mc, Mmin, Mmax}]} 2417 end. 2418 2419read_trace() -> 2420 read_trace(0,0,0,0). 2421 2422read_trace(Low, Normal, High, Max) -> 2423 receive 2424 {trace, Proc, in, _} -> 2425 {Prio, Count} = get(Proc), 2426 put(Proc, {Prio, Count+1}), 2427 case Prio of 2428 low -> read_trace(Low+1, Normal, High, Max); 2429 normal -> read_trace(Low, Normal+1, High, Max); 2430 high -> read_trace(Low, Normal, High+1, Max); 2431 max -> read_trace(Low, Normal, High, Max+1) 2432 end; 2433 {trace, _Proc, out, _} -> 2434 read_trace(Low, Normal, High, Max) 2435 after 0 -> 2436 {Low, Normal, High, Max} 2437 end. 2438 2439save_tracees(_Prio, []) -> 2440 ok; 2441save_tracees(Prio, [T|Ts]) -> 2442 put(T, {Prio, 0}), 2443 save_tracees(Prio, Ts). 2444 2445start_tracer() -> 2446 Tracer = spawn_link(fun () -> tracer([], [], [], []) end), 2447 true = erlang:suspend_process(Tracer), 2448 Tracer. 2449 2450get_trace_result(Tracer) -> 2451 erlang:resume_process(Tracer), 2452 Ref = make_ref(), 2453 Tracer ! {get_result, Ref, self()}, 2454 receive 2455 {trace_result, Ref, Res} -> 2456 Res 2457 end. 2458 2459 2460set_tracing(_On, _Tracer, _Prio, []) -> 2461 ok; 2462set_tracing(true, Tracer, Prio, Pids) -> 2463 Tracer ! {tracees, Prio, Pids}, 2464 set_tracing(true, Tracer, Pids); 2465set_tracing(false, Tracer, _Prio, Pids) -> 2466 set_tracing(false, Tracer, Pids). 2467 2468set_tracing(_On, _Tracer, []) -> 2469 ok; 2470set_tracing(On, Tracer, [Pid|Pids]) -> 2471 1 = erlang:trace(Pid, On, [running, {tracer, Tracer}]), 2472 set_tracing(On, Tracer, Pids). 2473 2474active_schedulers() -> 2475 case erlang:system_info(schedulers_online) of 2476 1 -> 2477 1; 2478 N -> 2479 case erlang:system_info(multi_scheduling) of 2480 blocked -> 1; 2481 enabled -> N 2482 end 2483 end. 2484 2485start_node(Config) -> 2486 start_node(Config, ""). 2487 2488start_node(Config, Args) when is_list(Config) -> 2489 Pa = filename:dirname(code:which(?MODULE)), 2490 Name = list_to_atom(atom_to_list(?MODULE) 2491 ++ "-" 2492 ++ atom_to_list(proplists:get_value(testcase, Config)) 2493 ++ "-" 2494 ++ integer_to_list(erlang:system_time(second)) 2495 ++ "-" 2496 ++ integer_to_list(erlang:unique_integer([positive]))), 2497 test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). 2498 2499stop_node(Node) -> 2500 test_server:stop_node(Node). 2501 2502 2503enable_internal_state() -> 2504 case catch erts_debug:get_internal_state(available_internal_state) of 2505 true -> true; 2506 _ -> erts_debug:set_internal_state(available_internal_state, true) 2507 end. 2508 2509cmp(X, X) -> 2510 ok; 2511cmp(X, Y) -> 2512 io:format("cmp failed:~n X=~p~n Y=~p~n", [X,Y]), 2513 cmp_aux(X, Y). 2514 2515 2516cmp_aux([X0|Y0], [X1|Y1]) -> 2517 cmp_aux(X0, X1), 2518 cmp_aux(Y0, Y1); 2519cmp_aux(T0, T1) when is_tuple(T0), is_tuple(T1), size(T0) == size(T1) -> 2520 cmp_tuple(T0, T1, 1, size(T0)); 2521cmp_aux(X, X) -> 2522 ok; 2523cmp_aux(F0, F1) -> 2524 ct:fail({no_match, F0, F1}). 2525 2526cmp_tuple(_T0, _T1, N, Sz) when N > Sz -> 2527 ok; 2528cmp_tuple(T0, T1, N, Sz) -> 2529 cmp_aux(element(N, T0), element(N, T1)), 2530 cmp_tuple(T0, T1, N+1, Sz). 2531