PageRenderTime 134ms CodeModel.GetById 38ms app.highlight 86ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rdbms/mnesia_patches/src/mnesia_lib.erl

http://github.com/gebi/jungerl
Erlang | 1377 lines | 1085 code | 182 blank | 110 comment | 17 complexity | d4c5ea24674b36201950c8c9da542b1f MD5 | raw file
   1%% ``The contents of this file are subject to the Erlang Public License,
   2%% Version 1.1, (the "License"); you may not use this file except in
   3%% compliance with the License. You should have received a copy of the
   4%% Erlang Public License along with this software. If not, it can be
   5%% retrieved via the world wide web at http://www.erlang.org/.
   6%% 
   7%% Software distributed under the License is distributed on an "AS IS"
   8%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   9%% the License for the specific language governing rights and limitations
  10%% under the License.
  11%% 
  12%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14%% AB. All Rights Reserved.''
  15%% 
  16%%     $Id$
  17%%
  18%% This module contains all sorts of various which doesn't fit
  19%% anywhere else. Basically everything is exported.
  20
  21-module(mnesia_lib).
  22
  23-include("mnesia.hrl").
  24-include_lib("kernel/include/file.hrl").
  25
  26-export([core_file/0]).
  27
  28-export([
  29	 active_tables/0,
  30	 add/2,
  31	 add_list/2,
  32	 all_nodes/0,
  33%%	 catch_val/1,
  34	 cleanup_tmp_files/1,	 
  35	 copy_file/2,
  36	 copy_holders/1,
  37	 coredump/0,
  38	 coredump/1,
  39	 create_counter/1,
  40	 cs_to_nodes/1,
  41	 cs_to_storage_type/2,
  42	 dets_to_ets/6, dets_to_ets/7,
  43	 db_chunk/2,
  44	 db_init_chunk/1,
  45	 db_init_chunk/2,
  46	 db_init_chunk/3,
  47	 db_erase/2,
  48	 db_erase/3,
  49	 db_erase_tab/1,
  50	 db_erase_tab/2,
  51	 db_first/1,
  52	 db_first/2,
  53	 db_last/1,
  54	 db_last/2,
  55	 db_fixtable/3,
  56	 db_get/2,
  57	 db_get/3,
  58	 db_match_erase/2,
  59	 db_match_erase/3,
  60	 db_match_object/2,
  61	 db_match_object/3,
  62	 db_next_key/2,
  63	 db_next_key/3,
  64	 db_prev_key/2,
  65	 db_prev_key/3,
  66	 db_put/2,
  67	 db_put/3,
  68	 db_select/2,	 
  69	 db_select/3,
  70	 db_select_init/4,
  71	 db_select_cont/3,
  72	 db_slot/2,
  73	 db_slot/3,
  74	 db_update_counter/3,
  75	 db_update_counter/4,
  76	 dbg_out/2,
  77	 del/2,
  78	 dets_sync_close/1,
  79	 dets_sync_open/2,
  80	 dets_sync_open/3,
  81	 dir/0,
  82	 dir/1,
  83	 dir_info/0,
  84	 dirty_rpc_error_tag/1,
  85	 dist_coredump/0,
  86	 disk_type/1,
  87	 disk_type/2,	 
  88	 elems/2,
  89	 ensure_loaded/1,
  90	 error/2,
  91	 error_desc/1,
  92	 etype/1,
  93	 exists/1,
  94	 fatal/2,
  95	 get_node_number/0,
  96	 fix_error/1,
  97	 important/2,
  98	 incr_counter/1,
  99	 incr_counter/2,
 100	 intersect/2,
 101	 is_running/0,
 102	 is_running/1,
 103	 is_running_remote/0,
 104	 is_string/1,
 105	 key_search_delete/3,
 106	 key_search_all/3,
 107	 last_error/0,
 108	 local_active_tables/0,
 109	 lock_table/1,
 110	 mkcore/1,
 111	 not_active_here/1,
 112	 other_val/2,
 113	 pad_name/3,
 114	 random_time/2,
 115	 read_counter/1,
 116	 readable_indecies/1,
 117	 remote_copy_holders/1,
 118	 report_fatal/2,
 119	 report_system_event/1,
 120	 running_nodes/0,
 121	 running_nodes/1,
 122	 schema_cs_to_storage_type/2,
 123	 search_delete/2,
 124	 set/2,
 125	 set_counter/2,
 126	 set_local_content_whereabouts/1,
 127	 set_remote_where_to_read/1,
 128	 set_remote_where_to_read/2,
 129	 show/1,
 130	 show/2,
 131	 sort_commit/1,
 132	 storage_type_at_node/2,
 133	 swap_tmp_files/1,
 134	 tab2dat/1,
 135	 tab2dmp/1,
 136	 tab2tmp/1,
 137	 tab2dcd/1,
 138	 tab2dcl/1,
 139	 to_list/1,
 140	 union/2,
 141	 uniq/1,
 142	 unlock_table/1,
 143	 unset/1,
 144	 update_counter/2,
 145	 val/1,
 146	 vcore/0,
 147	 vcore/1,
 148	 verbose/2,
 149	 view/0,
 150	 view/1,
 151	 view/2,
 152	 warning/2,
 153
 154	 is_debug_compiled/0,
 155	 activate_debug_fun/5,
 156	 deactivate_debug_fun/3,
 157	 eval_debug_fun/4,
 158	 scratch_debug_fun/0
 159	]).
 160 
 161
 162search_delete(Obj, List) ->
 163    search_delete(Obj, List, [], none).
 164search_delete(Obj, [Obj|Tail], Ack, _Res) ->
 165    search_delete(Obj, Tail, Ack, Obj);
 166search_delete(Obj, [H|T], Ack, Res) ->
 167    search_delete(Obj, T, [H|Ack], Res);
 168search_delete(_, [], Ack, Res) ->
 169    {Res, Ack}.
 170
 171key_search_delete(Key, Pos, TupleList) ->
 172    key_search_delete(Key, Pos, TupleList, none, []).
 173key_search_delete(Key, Pos, [H|T], _Obj, Ack) when element(Pos, H) == Key ->
 174    key_search_delete(Key, Pos, T, H, Ack);
 175key_search_delete(Key, Pos, [H|T], Obj, Ack) ->
 176    key_search_delete(Key, Pos, T, Obj, [H|Ack]);
 177key_search_delete(_, _, [], Obj, Ack) ->
 178    {Obj, Ack}.
 179
 180key_search_all(Key, Pos, TupleList) -> 
 181    key_search_all(Key, Pos, TupleList, []).
 182key_search_all(Key, N, [H|T], Ack) when element(N, H) == Key ->
 183    key_search_all(Key, N, T, [H|Ack]);
 184key_search_all(Key, N, [_|T], Ack) ->
 185    key_search_all(Key, N, T, Ack);
 186key_search_all(_, _, [], Ack) -> Ack.
 187
 188intersect(L1, L2) ->
 189    L2 -- (L2 -- L1).
 190
 191elems(I, [H|T]) ->
 192    [element(I, H) | elems(I, T)];
 193elems(_, []) ->
 194    [].
 195
 196%%  sort_commit see to that checkpoint info is always first in 
 197%%  commit_work structure the other info don't need to be sorted.
 198sort_commit(List) ->
 199    sort_commit2(List, []).
 200
 201sort_commit2([{checkpoints, ChkpL}| Rest], Acc) ->
 202    [{checkpoints, ChkpL}| Rest] ++ Acc;
 203sort_commit2([H | R], Acc) ->
 204    sort_commit2(R, [H | Acc]);
 205sort_commit2([], Acc) -> Acc.
 206    
 207is_string([H|T]) ->
 208    if
 209	0 =< H, H < 256, integer(H)  -> is_string(T);
 210	true -> false
 211    end;
 212is_string([]) -> true.
 213
 214%%%
 215
 216union([H|L1], L2) ->
 217    case lists:member(H, L2) of
 218	true -> union(L1, L2);
 219	false -> [H | union(L1, L2)]
 220    end;
 221union([], L2) -> L2.
 222
 223uniq([]) ->
 224    [];
 225uniq(List) ->
 226    [H|T] = lists:sort(List),
 227    uniq1(H, T, []).
 228
 229uniq1(H, [H|R], Ack) ->
 230    uniq1(H, R, Ack);
 231uniq1(Old, [H|R], Ack) ->
 232    uniq1(H, R, [Old|Ack]);
 233uniq1(Old, [], Ack) ->
 234    [Old| Ack].
 235
 236to_list(X) when list(X) -> X;
 237to_list(X) -> atom_to_list(X).
 238
 239all_nodes() ->
 240    Ns = mnesia:system_info(db_nodes) ++
 241	mnesia:system_info(extra_db_nodes),
 242    mnesia_lib:uniq(Ns).
 243
 244running_nodes() ->
 245    running_nodes(all_nodes()).
 246
 247running_nodes(Ns) ->
 248    {Replies, _BadNs} = rpc:multicall(Ns, ?MODULE, is_running_remote, []),
 249    [N || {GoodState, N} <- Replies, GoodState == true].
 250
 251is_running_remote() ->
 252    IsRunning = is_running(),
 253    {IsRunning == yes, node()}.
 254
 255is_running(Node) when atom(Node) ->
 256    case rpc:call(Node, ?MODULE, is_running, []) of
 257	{badrpc, _} -> no;
 258	X -> X
 259    end.
 260
 261is_running() ->
 262    case ?catch_val(mnesia_status) of
 263	{'EXIT', _} -> no;
 264	running -> yes;
 265	starting -> starting;
 266	stopping -> stopping
 267    end.
 268
 269show(X) ->
 270    show(X, []).
 271show(F, A) ->
 272    io:format(user, F, A).
 273
 274
 275pad_name([Char | Chars], Len, Tail) ->
 276    [Char | pad_name(Chars, Len - 1, Tail)];
 277pad_name([], Len, Tail) when Len =< 0 ->
 278    Tail;
 279pad_name([], Len, Tail) ->
 280    [$ | pad_name([], Len - 1, Tail)].
 281    
 282%% Some utility functions .....
 283active_here(Tab) ->
 284    case val({Tab, where_to_read}) of
 285	Node when Node == node() -> true;
 286	_ -> false
 287    end.
 288
 289not_active_here(Tab) ->
 290    not active_here(Tab).
 291
 292exists(Fname) ->
 293    case file:rawopen(Fname, read) of
 294	{ok, F} ->file:close(F), true;
 295	_ -> false
 296    end.
 297
 298dir() -> mnesia_monitor:get_env(dir).
 299
 300dir(Fname) ->
 301    filename:join([dir(), to_list(Fname)]).
 302
 303tab2dat(Tab) ->  %% DETS files 
 304    dir(lists:concat([Tab, ".DAT"])).
 305
 306tab2tmp(Tab) ->
 307    dir(lists:concat([Tab, ".TMP"])).
 308
 309tab2dmp(Tab) ->  %% Dumped ets tables
 310    dir(lists:concat([Tab, ".DMP"])).
 311
 312tab2dcd(Tab) ->  %% Disc copies data
 313    dir(lists:concat([Tab, ".DCD"])).
 314
 315tab2dcl(Tab) ->  %% Disc copies log
 316    dir(lists:concat([Tab, ".DCL"])).
 317
 318storage_type_at_node(Node, Tab) ->
 319    search_key(Node, [{disc_copies, val({Tab, disc_copies})},
 320		      {ram_copies, val({Tab, ram_copies})},
 321		      {disc_only_copies, val({Tab, disc_only_copies})}|
 322		      [{{external_copies, Mod}, Ns} ||
 323			  {Mod,Ns} <- val({Tab, external_copies})]]).
 324							  
 325
 326cs_to_storage_type(Node, Cs) ->
 327    search_key(Node, [{disc_copies, Cs#cstruct.disc_copies},
 328		      {ram_copies, Cs#cstruct.ram_copies},
 329		      {disc_only_copies, Cs#cstruct.disc_only_copies} |
 330		      [{{external_copies, Mod}, Ns} ||
 331			  {Mod,Ns} <- Cs#cstruct.external_copies]]).
 332
 333schema_cs_to_storage_type(Node, Cs) ->
 334    case cs_to_storage_type(Node, Cs) of
 335	unknown when Cs#cstruct.name == schema -> ram_copies;
 336	Other -> Other
 337    end.
 338
 339
 340search_key(Key, [{Val, List} | Tail]) ->
 341    case lists:member(Key, List) of
 342	true -> Val;
 343	false -> search_key(Key, Tail)
 344    end;
 345search_key(_Key, []) ->
 346    unknown.
 347
 348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 349%% ops, we've got some global variables here :-)
 350
 351%% They are
 352%%
 353%%   {Tab, setorbag}, -> set | bag
 354%%   {Tab, storage_type}       -> disc_copies |ram_copies | unknown (**)
 355%%   {Tab, disc_copies}        -> node list  (from schema)
 356%%   {Tab, ram_copies}, -> node list  (from schema)
 357%%   {Tab, arity}, -> number
 358%%   {Tab, attributes}, -> atom list
 359%%   {Tab, wild_pattern}, -> record tuple with '_'s
 360%%   {Tab, {index, Pos}}       -> ets table
 361%%   {Tab, index}              -> integer list
 362%%   {Tab, cstruct}            -> cstruct  structure
 363%%
 364
 365%%   The following fields are dynamic according to the
 366%%   the current node/table situation
 367
 368%%   {Tab, where_to_write}      -> node list
 369%%   {Tab, where_to_read}       -> node | nowhere
 370%%
 371%%   {schema, tables}                    -> tab list
 372%%   {schema, local_tables}              -> tab list  (**)
 373%%
 374%%   {current, db_nodes}                  -> node list
 375%%
 376%%   dir                                  -> directory path (**)
 377%%   mnesia_status                        -> status | running | stopping (**)
 378%%   (**) ==   (Different on all nodes)
 379%%
 380
 381val(Var) ->
 382    case ?catch_val(Var) of
 383	{'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); 
 384	_VaLuE_ -> _VaLuE_ 
 385    end.
 386
 387set(Var, Val) ->
 388    ?ets_insert(mnesia_gvar, {Var, Val}).
 389
 390unset(Var) ->
 391    ?ets_delete(mnesia_gvar, Var).
 392
 393other_val(Var, Other) ->
 394    case Var of
 395	{_, where_to_read} -> nowhere;
 396	{_, where_to_write} -> [];
 397	{_, active_replicas} -> [];
 398	_ ->
 399	    pr_other(Var, Other)
 400    end.
 401
 402pr_other(Var, Other) ->
 403    Why = 
 404	case is_running() of
 405	    no -> {node_not_running, node()};
 406	    _ -> {no_exists, Var}
 407	end,
 408    verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~p ~n",
 409	    [self(), process_info(self(), registered_name),
 410	     Var, Other, Why]),
 411    case Other of
 412	{badarg, [{ets, lookup_element, _}|_]} ->
 413	    exit(Why);
 414	_ ->
 415	    erlang:fault(Why)
 416    end.
 417
 418%% Some functions for list valued variables
 419add(Var, Val) ->
 420    L = val(Var),
 421    set(Var, [Val | lists:delete(Val, L)]).
 422
 423add_list(Var, List) ->
 424    L = val(Var),
 425    set(Var, union(L, List)).
 426
 427del(Var, Val) ->
 428    L = val(Var),
 429    set(Var, lists:delete(Val, L)).
 430
 431%% This function is needed due to the fact
 432%% that the application_controller enters
 433%% a deadlock now and then. ac is implemented
 434%% as a rather naive server.
 435ensure_loaded(Appl) ->
 436    case application_controller:get_loaded(Appl) of
 437	{true, _} -> 
 438	    ok;
 439	false ->
 440	    case application:load(Appl) of
 441		ok ->
 442		    ok;
 443		{error, {already_loaded, Appl}} ->
 444		    ok;
 445		{error, Reason} ->
 446		    {error, {application_load_error, Reason}}
 447	    end
 448    end.
 449
 450local_active_tables() ->
 451    Tabs = val({schema, local_tables}),
 452    lists:zf(fun(Tab) -> active_here(Tab) end, Tabs).
 453
 454active_tables() ->
 455    Tabs = val({schema, tables}),
 456    F = fun(Tab) ->
 457		case val({Tab, where_to_read}) of
 458		    nowhere -> false;
 459		    _ -> {true, Tab}
 460		end
 461	end,
 462    lists:zf(F, Tabs).
 463
 464etype(X) when integer(X) -> integer;
 465etype([]) -> nil;
 466etype(X) when list(X) -> list;
 467etype(X) when tuple(X) -> tuple;
 468etype(X) when atom(X) -> atom;
 469etype(_) -> othertype.
 470
 471remote_copy_holders(Cs) ->
 472    copy_holders(Cs) -- [node()].
 473
 474copy_holders(Cs) when Cs#cstruct.local_content == false ->
 475    cs_to_nodes(Cs);
 476copy_holders(Cs) when Cs#cstruct.local_content == true ->
 477    case lists:member(node(), cs_to_nodes(Cs)) of
 478	true -> [node()];
 479	false -> []
 480    end.
 481
 482
 483set_remote_where_to_read(Tab) ->
 484    set_remote_where_to_read(Tab, []).
 485
 486set_remote_where_to_read(Tab, Ignore) ->
 487    Active = val({Tab, active_replicas}),
 488    Valid = 
 489	case mnesia_recover:get_master_nodes(Tab) of
 490	    [] ->  Active;
 491	    Masters -> mnesia_lib:intersect(Masters, Active)
 492	end,    
 493    Available = mnesia_lib:intersect(val({current, db_nodes}), Valid -- Ignore),    
 494    DiscOnlyC = val({Tab, disc_only_copies}),
 495    Prefered  = Available -- DiscOnlyC,
 496    if
 497	Prefered /= [] ->
 498	    set({Tab, where_to_read}, hd(Prefered));
 499	Available /= [] ->
 500	    set({Tab, where_to_read}, hd(Available));
 501	true ->
 502	    set({Tab, where_to_read}, nowhere)
 503    end.
 504
 505%%% Local only
 506set_local_content_whereabouts(Tab) ->
 507    add({schema, local_tables}, Tab),
 508    add({Tab, active_replicas}, node()),
 509    set({Tab, where_to_write}, [node()]),
 510    set({Tab, where_to_read}, node()).
 511
 512%%% counter routines
 513
 514create_counter(Name) ->
 515    set_counter(Name, 0).
 516
 517set_counter(Name, Val) ->
 518    ?ets_insert(mnesia_gvar, {Name, Val}).
 519
 520incr_counter(Name) ->
 521    ?ets_update_counter(mnesia_gvar, Name, 1).
 522
 523incr_counter(Name, I) ->
 524    ?ets_update_counter(mnesia_gvar, Name, I).
 525
 526update_counter(Name, Val) ->
 527    ?ets_update_counter(mnesia_gvar, Name, Val).
 528
 529read_counter(Name) ->
 530    ?ets_lookup_element(mnesia_gvar, Name, 2).
 531
 532cs_to_nodes(Cs) ->
 533    Cs#cstruct.disc_only_copies ++
 534	Cs#cstruct.disc_copies ++
 535	Cs#cstruct.ram_copies ++
 536	ext_copy_nodes(Cs#cstruct.external_copies).
 537
 538ext_copy_nodes(Ext) ->
 539    ordsets:from_list(lists:concat([Ns || {_, Ns} <- Ext])).
 540 
 541dist_coredump() ->
 542    dist_coredump(all_nodes()).
 543dist_coredump(Ns) ->
 544    {Replies, _} = rpc:multicall(Ns, ?MODULE, coredump, []),
 545    Replies.
 546
 547coredump() ->
 548    coredump({crashinfo, {"user initiated~n", []}}).
 549coredump(CrashInfo) ->
 550    Core = mkcore(CrashInfo),
 551    Out = core_file(),
 552    important("Writing Mnesia core to file: ~p...~p~n", [Out, CrashInfo]),
 553    file:write_file(Out, Core),
 554    Out.
 555
 556core_file() ->
 557    Integers = tuple_to_list(date()) ++ tuple_to_list(time()),
 558    Fun = fun(I) when I < 10 -> ["_0", I];
 559	     (I) -> ["_", I]
 560	  end,
 561    List = lists:append([Fun(I) || I <- Integers]),
 562    case mnesia_monitor:get_env(core_dir) of
 563	Dir when list(Dir) ->
 564	    filename:absname(lists:concat(["MnesiaCore.", node()] ++ List), Dir);
 565	_ ->
 566	    filename:absname(lists:concat(["MnesiaCore.", node()] ++ List))
 567    end.
 568   
 569mkcore(CrashInfo) ->
 570%   dbg_out("Making a Mnesia core dump...~p~n", [CrashInfo]),
 571    Nodes = [node() |nodes()],
 572    TidLocks = (catch ets:tab2list(mnesia_tid_locks)),
 573    Core = [
 574	    CrashInfo,
 575	    {time, {date(), time()}},
 576	    {self, catch process_info(self())},
 577	    {nodes, catch rpc:multicall(Nodes, ?MODULE, get_node_number, [])},
 578	    {applications, catch lists:sort(application:loaded_applications())},
 579	    {flags, catch init:get_arguments()},
 580	    {code_path, catch code:get_path()},
 581	    {code_loaded, catch lists:sort(code:all_loaded())},
 582	    {etsinfo, catch ets_info(ets:all())},
 583
 584	    {version, catch mnesia:system_info(version)},
 585	    {schema, catch ets:tab2list(schema)},
 586	    {gvar, catch ets:tab2list(mnesia_gvar)},
 587	    {master_nodes, catch mnesia_recover:get_master_node_info()},
 588
 589	    {processes, catch procs()},
 590	    {relatives, catch relatives()},
 591	    {workers, catch workers(mnesia_controller:get_workers(2000))},
 592	    {locking_procs, catch locking_procs(TidLocks)},
 593
 594	    {held_locks, catch mnesia:system_info(held_locks)},
 595	    {tid_locks, TidLocks},
 596	    {lock_queue, catch mnesia:system_info(lock_queue)},
 597	    {load_info, catch mnesia_controller:get_info(2000)},
 598	    {trans_info, catch mnesia_tm:get_info(2000)},
 599	    	    
 600	    {schema_file, catch file:read_file(tab2dat(schema))},
 601	    {dir_info, catch dir_info()},
 602	    {logfile, catch {ok, read_log_files()}}
 603	   ],
 604    term_to_binary(Core).
 605
 606procs() ->
 607    Fun = fun(P) -> {P, (catch lists:zf(fun proc_info/1, process_info(P)))} end,
 608    lists:map(Fun, processes()).
 609
 610proc_info({registered_name, Val}) -> {true, Val};
 611proc_info({message_queue_len, Val}) -> {true, Val};
 612proc_info({status, Val}) -> {true, Val};
 613proc_info({current_function, Val}) -> {true, Val};
 614proc_info(_) -> false.
 615
 616get_node_number() ->
 617    {node(), self()}.
 618
 619read_log_files() ->
 620    [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()].
 621
 622dir_info() ->
 623    {ok, Cwd} = file:get_cwd(),
 624    Dir = dir(),
 625    [{cwd, Cwd, file:read_file_info(Cwd)},
 626     {mnesia_dir, Dir, file:read_file_info(Dir)}] ++
 627    case file:list_dir(Dir) of
 628	{ok, Files} ->
 629	    [{mnesia_file, F, catch file:read_file_info(dir(F))} || F <- Files];
 630	Other ->
 631	    [Other]
 632    end.
 633
 634ets_info([H|T]) ->
 635    [{table, H, mk_info_tuple(ets:info(H))} | ets_info(T)];
 636ets_info([]) -> [].
 637
 638mk_info_tuple(T) when is_list(T) ->
 639    list_to_tuple(T);
 640mk_info_tuple(T) -> T.
 641
 642relatives() ->
 643    Info = fun(Name) ->
 644		   case whereis(Name) of
 645		       undefined -> false;
 646		       Pid -> {true, {Name, Pid, catch process_info(Pid)}}
 647		   end
 648	   end,
 649    lists:zf(Info, mnesia:ms()).
 650
 651workers({workers, Loader, Senders, Dumper}) ->
 652    Info = fun({Pid, {send_table, Tab, _Receiver, _St}}) ->
 653		   case Pid of
 654		       undefined -> false;
 655		       Pid -> {true, {Pid, Tab, catch process_info(Pid)}}
 656		   end;
 657	       ({Name, Pid}) ->
 658		   case Pid of
 659		       undefined -> false;
 660		       Pid -> {true, {Name, Pid, catch process_info(Pid)}}
 661		   end
 662	   end,
 663    SInfo = lists:zf(Info, Senders),
 664    [{senders, SInfo} | lists:zf(Info, [{loader, Loader}, {dumper, Dumper}])].
 665
 666locking_procs(LockList) when list(LockList) ->
 667    Tids = [element(1, Lock) || Lock <- LockList],
 668    UT = uniq(Tids),    
 669    Info = fun(Tid) ->
 670		   Pid = Tid#tid.pid,
 671		   case node(Pid) == node() of
 672		       true -> 
 673			   {true, {Pid, catch process_info(Pid)}};
 674		       _ ->
 675			   false
 676		   end
 677	   end,
 678    lists:zf(Info, UT).
 679
 680view() ->
 681    Bin = mkcore({crashinfo, {"view only~n", []}}),
 682    vcore(Bin).
 683
 684%% Displays a Mnesia file on the tty. The file may be repaired.
 685view(File) ->
 686    case suffix([".DAT", ".RET", ".DMP", ".TMP"], File) of
 687	true ->
 688	    view(File, dat);
 689	false ->
 690	    case suffix([".LOG", ".BUP", ".ETS"], File) of
 691		true ->
 692		    view(File, log);
 693		false ->
 694		    case lists:prefix("MnesiaCore.", File) of
 695			true ->
 696			    view(File, core);
 697			false ->
 698			    {error, "Unknown file name"}
 699		    end
 700	    end
 701    end.
 702
 703view(File, dat) ->
 704    dets:view(File);
 705view(File, log) ->
 706    mnesia_log:view(File);
 707view(File, core) ->
 708    vcore(File).
 709
 710suffix(Suffixes, File) ->
 711    Fun = fun(S) -> lists:suffix(S, File) end,
 712    lists:any(Fun, Suffixes).
 713
 714%% View a core file
 715
 716vcore() ->
 717    Prefix = lists:concat(["MnesiaCore.", node()]),
 718    Filter = fun(F) -> lists:prefix(Prefix, F) end,
 719    {ok, Cwd} = file:get_cwd(),
 720    case file:list_dir(Cwd) of
 721	{ok, Files}->
 722	    CoreFiles = lists:sort(lists:zf(Filter, Files)),
 723	    show("Mnesia core files: ~p~n", [CoreFiles]),
 724	    vcore(lists:last(CoreFiles));
 725	Error ->
 726	    Error
 727    end.
 728
 729vcore(Bin) when binary(Bin) ->
 730    Core = binary_to_term(Bin),
 731    Fun = fun({Item, Info}) ->
 732		  show("***** ~p *****~n", [Item]),
 733		  case catch vcore_elem({Item, Info}) of
 734		      {'EXIT', Reason} ->
 735			  show("{'EXIT', ~p}~n", [Reason]);
 736		      _ -> ok
 737		  end
 738	  end,
 739    lists:foreach(Fun, Core);
 740    
 741vcore(File) ->
 742    show("~n***** Mnesia core: ~p *****~n", [File]),
 743    case file:read_file(File) of
 744	{ok, Bin} ->
 745	    vcore(Bin);
 746	_ ->
 747	    nocore
 748    end.
 749
 750vcore_elem({schema_file, {ok, B}}) ->
 751    Fname = "/tmp/schema.DAT",
 752    file:write_file(Fname, B),
 753    dets:view(Fname),
 754    file:delete(Fname);
 755
 756vcore_elem({logfile, {ok, BinList}}) ->
 757    Fun = fun({F, Info}) ->
 758		  show("----- logfile: ~p -----~n", [F]),
 759		  case Info of
 760		      {ok, B} ->
 761			  Fname = "/tmp/mnesia_vcore_elem.TMP",
 762			  file:write_file(Fname, B),
 763			  mnesia_log:view(Fname),
 764			  file:delete(Fname);
 765		      _ ->
 766			  show("~p~n", [Info])
 767		  end
 768	  end,
 769    lists:foreach(Fun, BinList);
 770
 771vcore_elem({crashinfo, {Format, Args}}) ->
 772    show(Format, Args);
 773vcore_elem({gvar, L}) ->
 774    show("~p~n", [lists:sort(L)]);
 775vcore_elem({transactions, Info}) ->
 776    mnesia_tm:display_info(user, Info);
 777
 778vcore_elem({_Item, Info}) ->
 779    show("~p~n", [Info]).
 780
 781fix_error(X) ->
 782    set(last_error, X), %% for debugabililty
 783    case X of
 784	{aborted, Reason} -> Reason;
 785	{abort, Reason} -> Reason;
 786	Y when atom(Y) -> Y;
 787	{'EXIT', {_Reason, {Mod, _, _}}} when atom(Mod) ->
 788	    save(X),
 789	    case atom_to_list(Mod) of
 790		[$m, $n, $e|_] -> badarg;
 791		_ -> X
 792	    end;
 793	_ -> X
 794    end.
 795
 796last_error() ->
 797    val(last_error).
 798
 799%% The following is a list of possible mnesia errors and what they
 800%% actually mean
 801
 802error_desc(nested_transaction) -> "Nested transactions are not allowed";
 803error_desc(badarg) -> "Bad or invalid argument, possibly bad type";
 804error_desc(no_transaction) -> "Operation not allowed outside transactions";
 805error_desc(combine_error)  -> "Table options were ilegally combined";
 806error_desc(bad_index)  -> "Index already exists or was out of bounds";
 807error_desc(already_exists) -> "Some schema option we try to set is already on";
 808error_desc(index_exists)-> "Some ops can not  be performed on tabs with index";
 809error_desc(no_exists)-> "Tried to perform op on non-existing (non alive) item";
 810error_desc(system_limit) -> "Some system_limit was exhausted";
 811error_desc(mnesia_down) -> "A transaction involving objects at some remote "
 812                           "node which died while transaction was executing"
 813                           "*and* object(s) are no longer available elsewhere"
 814                           "in the network";
 815error_desc(not_a_db_node) -> "A node which is non existant in "
 816                              "the schema was mentioned";
 817error_desc(bad_type)            -> "Bad type on some provided arguments";
 818error_desc(node_not_running)    -> "Node not running";
 819error_desc(truncated_binary_file) -> "Truncated binary in file";
 820error_desc(active)     -> "Some delete ops require that "
 821                           "all active objects are removed";
 822error_desc(illegal) -> "Operation not supported on object";
 823error_desc({'EXIT', Reason}) ->
 824    error_desc(Reason);
 825error_desc({error, Reason}) ->
 826    error_desc(Reason);
 827error_desc({aborted, Reason}) ->
 828    error_desc(Reason);
 829error_desc(Reason) when tuple(Reason), size(Reason) > 0 ->
 830    setelement(1, Reason, error_desc(element(1, Reason)));
 831error_desc(Reason) ->
 832    Reason.
 833
 834dirty_rpc_error_tag(Reason) ->
 835    case Reason of
 836	{'EXIT', _} -> badarg;
 837	no_variable -> badarg;
 838	_           -> no_exists
 839    end.
 840
 841fatal(Format, Args) ->
 842    catch set(mnesia_status, stopping),
 843    Core = mkcore({crashinfo, {Format, Args}}),
 844    report_fatal(Format, Args, Core),
 845    timer:sleep(10000), % Enough to write the core dump to disc?
 846    mnesia:lkill(),
 847    exit(fatal).
 848
 849report_fatal(Format, Args) ->
 850    report_fatal(Format, Args, nocore).
 851
 852report_fatal(Format, Args, Core) ->
 853    report_system_event({mnesia_fatal, Format, Args, Core}),
 854    catch exit(whereis(mnesia_monitor), fatal).
 855
 856%% We sleep longer and longer the more we try
 857%% Made some testing and came up with the following constants
 858random_time(Retries, _Counter0) ->    
 859%    UpperLimit = 2000,
 860%    MaxIntv = trunc(UpperLimit * (1-(4/((Retries*Retries)+4)))),
 861    UpperLimit = 500,
 862    Dup = Retries * Retries,
 863    MaxIntv = trunc(UpperLimit * (1-(50/((Dup)+50)))),
 864    
 865    case get(random_seed) of
 866	undefined ->
 867	    {X, Y, Z} = erlang:now(), %% time()
 868	    random:seed(X, Y, Z),
 869	    Time = Dup + random:uniform(MaxIntv),
 870	    %%	    dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
 871	    Time;
 872	_ ->
 873	    Time = Dup + random:uniform(MaxIntv),
 874	    %%	    dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
 875	    Time	    
 876    end.
 877
 878report_system_event(Event0) ->
 879    Event = {mnesia_system_event, Event0},
 880    report_system_event(catch_notify(Event), Event),
 881    case ?catch_val(subscribers) of
 882	{'EXIT', _} -> ignore;
 883	Pids -> lists:foreach(fun(Pid) -> Pid ! Event end, Pids)
 884    end,
 885    ok.
 886
 887catch_notify(Event) ->
 888    case whereis(mnesia_event) of
 889	undefined ->
 890	    {'EXIT', {badarg, {mnesia_event, Event}}};
 891	Pid ->
 892	    gen_event:notify(Pid, Event)
 893    end.
 894
 895report_system_event({'EXIT', Reason}, Event) ->
 896    Mod = mnesia_monitor:get_env(event_module),
 897    case mnesia_sup:start_event() of
 898	{ok, Pid} ->
 899	    link(Pid),
 900	    gen_event:call(mnesia_event, Mod, Event, infinity),
 901	    unlink(Pid),
 902
 903            %% We get an exit signal if server dies
 904            receive
 905                {'EXIT', Pid, _Reason} ->
 906                    {error, {node_not_running, node()}}
 907            after 0 ->
 908		    gen_event:stop(mnesia_event),
 909                    ok
 910            end;
 911
 912	Error ->
 913	    Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n",
 914	    error_logger:format(Msg, [node(), Event, Reason, Error])
 915    end;
 916report_system_event(_Res, _Event) ->
 917    ignore.
 918
 919%% important messages are reported regardless of debug level
 920important(Format, Args) ->
 921    save({Format, Args}),
 922    report_system_event({mnesia_info, Format, Args}).
 923
 924%% Warning messages are reported regardless of debug level
 925warning(Format, Args) ->
 926    save({Format, Args}),
 927    report_system_event({mnesia_warning, Format, Args}).
 928
 929%% error messages are reported regardless of debug level
 930error(Format, Args) ->
 931    save({Format, Args}),
 932    report_system_event({mnesia_error, Format, Args}).
 933
 934%% verbose messages are reported if debug level == debug or verbose
 935verbose(Format, Args) ->
 936    case mnesia_monitor:get_env(debug) of
 937	none ->    save({Format, Args});
 938	verbose -> important(Format, Args);
 939	debug ->   important(Format, Args);
 940	trace ->   important(Format, Args)
 941    end.
 942
 943%% debug message are display if debug level == 2
 944dbg_out(Format, Args) ->
 945    case mnesia_monitor:get_env(debug) of
 946	none ->    ignore;
 947	verbose -> save({Format, Args});
 948	_ ->  report_system_event({mnesia_info, Format, Args})
 949    end.
 950
 951%% Keep the last 10 debug print outs
 952save(DbgInfo) ->
 953    catch save2(DbgInfo).
 954
 955save2(DbgInfo) ->
 956    Key = {'$$$_report', current_pos},
 957    P =
 958	case ?ets_lookup_element(mnesia_gvar, Key, 2) of
 959	    30 -> -1;
 960	    I -> I
 961	end,
 962    set({'$$$_report', current_pos}, P+1),
 963    set({'$$$_report', P+1}, {date(), time(), DbgInfo}).
 964
 965copy_file(From, To) ->
 966    case file:rawopen(From, {binary, read}) of
 967	{ok, F} ->
 968	    case file:rawopen(To, {binary, write}) of
 969		{ok, T} ->
 970		    Res = copy_file_loop(F, T, 8000),
 971		    file:close(F),
 972		    file:close(T),
 973		    Res;
 974		{error, Reason} ->
 975		    {error, Reason}
 976	    end;
 977	{error, Reason} ->
 978	    {error, Reason}
 979    end.
 980
 981copy_file_loop(F, T, ChunkSize) ->
 982    case file:read(F, ChunkSize) of
 983	{ok, {0, _}} ->
 984	    ok;
 985	{ok, {_, Bin}} ->
 986	    file:write(T, Bin),
 987	    copy_file_loop(F, T, ChunkSize);
 988	{ok, Bin} ->
 989	    file:write(T, Bin),
 990	    copy_file_loop(F, T, ChunkSize);
 991	eof ->
 992	    ok;
 993	{error, Reason} ->
 994	    {error, Reason}
 995    end.
 996
 997
 998%%%%%%%%%%%%
 999%% versions of all the lowlevel db funcs that determine whether we
1000%% shall go to disc or ram to do the actual operation.
1001
1002db_get(Tab, Key) ->
1003    db_get(val({Tab, storage_type}), Tab, Key).
1004db_get(ram_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
1005db_get(disc_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
1006db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key);
1007db_get({external_copies, Mod}, Tab, Key) -> Mod:db_get(Tab, Key).
1008
1009db_init_chunk(Tab) ->
1010    db_init_chunk(val({Tab, storage_type}), Tab, 1000).
1011db_init_chunk(Tab, N) ->
1012    db_init_chunk(val({Tab, storage_type}), Tab, N).
1013
1014db_init_chunk(disc_only_copies, Tab, N) ->
1015    dets:select(Tab, [{'_', [], ['$_']}], N);
1016db_init_chunk({external_copies, Mod}, Tab, N) ->
1017    Mod:db_init_chunk(Tab, N);
1018db_init_chunk(_, Tab, N) ->
1019    ets:select(Tab, [{'_', [], ['$_']}], N).
1020
1021db_chunk(disc_only_copies, State) ->
1022    dets:select(State);
1023db_chunk({external_copies, Mod}, State) ->
1024    Mod:db_chunk(State);
1025db_chunk(_, State) ->
1026    ets:select(State).
1027
1028db_put(Tab, Val) ->
1029    db_put(val({Tab, storage_type}), Tab, Val).
1030
1031db_put(ram_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
1032db_put(disc_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
1033db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val);
1034db_put({external_copies, Mod}, Tab, Val) ->
1035    Mod:db_put(Tab, Val).
1036
1037db_match_object(Tab, Pat) ->
1038    db_match_object(val({Tab, storage_type}), Tab, Pat).
1039db_match_object(Storage, Tab, Pat) ->
1040    db_fixtable(Storage, Tab, true),
1041    Res = catch_match_object(Storage, Tab, Pat),
1042    db_fixtable(Storage, Tab, false),
1043    case Res of
1044	{'EXIT', Reason} -> exit(Reason);
1045	_ -> Res
1046    end.
1047
1048catch_match_object(disc_only_copies, Tab, Pat) ->
1049    catch dets:match_object(Tab, Pat);
1050catch_match_object({external_copies, Mod}, Tab, Pat) ->
1051    catch Mod:match_object(Tab, Pat);
1052catch_match_object(_, Tab, Pat) ->
1053    catch ets:match_object(Tab, Pat).
1054
1055db_select(Tab, Pat) ->
1056    db_select(val({Tab, storage_type}), Tab, Pat).
1057
1058db_select(Storage, Tab, Pat) ->
1059    db_fixtable(Storage, Tab, true),
1060    Res = catch_select(Storage, Tab, Pat),
1061    db_fixtable(Storage, Tab, false),
1062    case Res of
1063	{'EXIT', Reason} -> exit(Reason);
1064	_ -> Res
1065    end.
1066
1067catch_select(disc_only_copies, Tab, Pat) ->
1068    catch dets:select(Tab, Pat);
1069catch_select({external_copies, Mod}, Tab, Pat) ->
1070    catch Mod:select(Tab, Pat);
1071catch_select(_, Tab, Pat) ->
1072    catch ets:select(Tab, Pat).
1073
1074db_select_init(disc_only_copies, Tab, Pat, Limit) ->
1075    dets:select(Tab, Pat, Limit);
1076db_select_init({external_copies, Mod}, Tab, Pat, Limit) ->
1077    Mod:select_init(Tab, Pat, Limit);
1078db_select_init(_, Tab, Pat, Limit) ->
1079    ets:select(Tab, Pat, Limit).
1080
1081db_select_cont(disc_only_copies, Cont0, Ms) ->
1082    Cont = dets:repair_continuation(Cont0, Ms),
1083    dets:select(Cont);
1084db_select_cont({external_copies, Mod}, Cont0, Ms) ->
1085    Cont = Mod:repair_continuation(Cont0, Ms),
1086    Mod:select(Cont);
1087db_select_cont(_, Cont0, Ms) ->
1088    Cont = ets:repair_continuation(Cont0, Ms),
1089    ets:select(Cont).
1090
1091db_fixtable(ets, Tab, Bool) ->
1092    ets:safe_fixtable(Tab, Bool);
1093db_fixtable(ram_copies, Tab, Bool) ->
1094    ets:safe_fixtable(Tab, Bool);
1095db_fixtable(disc_copies, Tab, Bool) ->
1096    ets:safe_fixtable(Tab, Bool);
1097db_fixtable(dets, Tab, Bool) ->
1098    dets:safe_fixtable(Tab, Bool);
1099db_fixtable(disc_only_copies, Tab, Bool) ->
1100    dets:safe_fixtable(Tab, Bool);
1101db_fixtable({external_copies, Mod}, Tab, Bool) ->
1102    Mod:safe_fixtable(Tab, Bool).
1103
1104db_erase(Tab, Key) ->
1105    db_erase(val({Tab, storage_type}), Tab, Key).
1106db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
1107db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
1108db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key);
1109db_erase({external_copies, Mod}, Tab, Key) ->
1110    Mod:db_erase(Tab, Key).
1111
1112db_match_erase(Tab, Pat) ->
1113    db_match_erase(val({Tab, storage_type}), Tab, Pat).
1114db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
1115db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
1116db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat);
1117db_match_erase({external_copies, Mod}, Tab, Pat) ->
1118    Mod:match_erase(Tab, Pat).
1119
1120db_first(Tab) ->
1121    db_first(val({Tab, storage_type}), Tab).
1122db_first(ram_copies, Tab) -> ?ets_first(Tab);
1123db_first(disc_copies, Tab) -> ?ets_first(Tab);
1124db_first(disc_only_copies, Tab) -> dets:first(Tab);
1125db_first({external_copies, Mod}, Tab) -> Mod:db_first(Tab).
1126
1127db_next_key(Tab, Key) ->
1128    db_next_key(val({Tab, storage_type}), Tab, Key).
1129db_next_key(ram_copies, Tab, Key) -> ?ets_next(Tab, Key);
1130db_next_key(disc_copies, Tab, Key) -> ?ets_next(Tab, Key);
1131db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key);
1132db_next_key({external_copies, Mod}, Tab, Key) ->
1133    Mod:db_next_key(Tab, Key).
1134
1135db_last(Tab) ->
1136    db_last(val({Tab, storage_type}), Tab).
1137db_last(ram_copies, Tab) -> ?ets_last(Tab);
1138db_last(disc_copies, Tab) -> ?ets_last(Tab);
1139db_last(disc_only_copies, Tab) -> dets:first(Tab); %% Dets don't have order
1140db_last({external_copies, Mod}, Tab) ->
1141    Mod:db_last(Tab).
1142
1143db_prev_key(Tab, Key) ->
1144    db_prev_key(val({Tab, storage_type}), Tab, Key).
1145db_prev_key(ram_copies, Tab, Key) -> ?ets_prev(Tab, Key);
1146db_prev_key(disc_copies, Tab, Key) -> ?ets_prev(Tab, Key);
1147db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key); %% Dets don't have order
1148db_prev_key({external_copies, Mod}, Tab, Key) ->
1149    Mod:db_prev_key(Tab, Key).
1150
1151db_slot(Tab, Pos) ->
1152    db_slot(val({Tab, storage_type}), Tab, Pos).
1153db_slot(ram_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
1154db_slot(disc_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
1155db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos);
1156db_slot({external_copies, Mod}, Tab, Pos) ->
1157    Mod:db_slot(Tab, Pos).
1158
1159db_update_counter(Tab, C, Val) ->
1160    db_update_counter(val({Tab, storage_type}), Tab, C, Val).
1161db_update_counter(ram_copies, Tab, C, Val) ->
1162    ?ets_update_counter(Tab, C, Val);
1163db_update_counter(disc_copies, Tab, C, Val) ->
1164    ?ets_update_counter(Tab, C, Val);
1165db_update_counter(disc_only_copies, Tab, C, Val) ->
1166    dets:update_counter(Tab, C, Val);
1167db_update_counter({external_copies, Mod}, Tab, C, Val) ->
1168    Mod:db_update_counter(Tab, C, Val).
1169
1170db_erase_tab(Tab) ->
1171    db_erase_tab(val({Tab, storage_type}), Tab).
1172db_erase_tab(ram_copies, Tab) -> ?ets_delete_table(Tab);
1173db_erase_tab(disc_copies, Tab) -> ?ets_delete_table(Tab);
1174db_erase_tab(disc_only_copies, _Tab) -> ignore;
1175db_erase_tab({external_copies, Mod}, Tab) ->
1176    Mod:db_erase_tab(Tab).
1177
1178%% assuming that Tab is a valid ets-table
1179dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) ->
1180    {Open, Close} = mkfuns(Lock),
1181    case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)},
1182			{keypos, 2}, {repair, Rep}]) of
1183	{ok, Tabname} ->
1184	    Res = dets:to_ets(Tabname, Tab),
1185	    Close(Tabname),
1186	    trav_ret(Res, Tab);
1187	Other ->
1188	    Other
1189    end.
1190
1191%% assuming that Tab is a valid ets-table
1192dets_to_ets(Tabname, Tab, File, Type, Rep, Lock, OnLoadFun) ->
1193    {Open, Close} = mkfuns(Lock),
1194    case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)},
1195			{keypos, 2}, {repair, Rep}]) of
1196	{ok, Tabname} ->
1197%%%	    Res = dets:to_ets(Tabname, Tab),
1198	    Res = chunk_dets_to_ets(dets:bchunk(Tabname, start), Tabname,
1199				    Tab, OnLoadFun),
1200	    Close(Tabname),
1201	    trav_ret(Res, Tab);
1202	Other ->
1203	    Other
1204    end.
1205
1206chunk_dets_to_ets('$end_of_table', _Tabname, Tab, _OnLoadFun) ->
1207    Tab;
1208chunk_dets_to_ets({error, Reason}, Tabname, _Tab, _OnLoadFun) ->
1209    {error, {Reason, Tabname}};
1210chunk_dets_to_ets({Cont, Objs}, Tabname, Tab, OnLoadFun) ->
1211    ets:insert(Tab, Objs),
1212    OnLoadFun([{write,O} || O <- Objs]),
1213    chunk_dets_to_ets(dets:bchunk(Tabname, Cont), Tabname, Tab, OnLoadFun).
1214			     
1215    
1216
1217trav_ret(Tabname, Tabname) -> loaded;
1218trav_ret(Other, _Tabname) -> Other.
1219
1220mkfuns(yes) ->
1221    {fun(Tab, Args) -> dets_sync_open(Tab, Args) end,
1222     fun(Tab) -> dets_sync_close(Tab) end};
1223mkfuns(no) ->
1224    {fun(Tab, Args) -> dets:open_file(Tab, Args) end,
1225     fun(Tab) -> dets:close(Tab) end}.
1226
1227disk_type(Tab) ->
1228    disk_type(Tab, val({Tab, setorbag})).
1229
1230disk_type(_Tab, ordered_set) ->
1231    set;
1232disk_type(_, Type) ->
1233    Type.
1234
1235dets_sync_open(Tab, Ref, File) ->
1236    Args = [{file, File},
1237	    {keypos, 2},
1238	    {repair, mnesia_monitor:get_env(auto_repair)},
1239	    {type, disk_type(Tab)}],
1240    dets_sync_open(Ref, Args).
1241
1242lock_table(Tab) ->
1243    global:set_lock({{mnesia_table_lock, Tab}, self()}, [node()], infinity).
1244%    dbg_out("dets_sync_open: ~p ~p~n", [T, self()]),
1245
1246unlock_table(Tab) ->
1247    global:del_lock({{mnesia_table_lock, Tab}, self()}, [node()]).
1248%    dbg_out("unlock_table: ~p ~p~n", [T, self()]),
1249
1250dets_sync_open(Tab, Args) ->
1251    lock_table(Tab),
1252    case dets:open_file(Tab, Args) of
1253	{ok, Tab} ->
1254	    {ok, Tab};
1255	Other ->
1256	    dets_sync_close(Tab),
1257	    Other
1258    end.
1259
1260dets_sync_close(Tab) ->
1261    catch dets:close(Tab),
1262    unlock_table(Tab),
1263    ok.
1264
1265cleanup_tmp_files([Tab | Tabs]) ->
1266    dets_sync_close(Tab),
1267    file:delete(tab2tmp(Tab)),
1268    cleanup_tmp_files(Tabs);
1269cleanup_tmp_files([]) ->
1270    ok.
1271
1272%% Returns a list of bad tables
1273swap_tmp_files([Tab | Tabs]) ->
1274    dets_sync_close(Tab),
1275    Tmp = tab2tmp(Tab),
1276    Dat = tab2dat(Tab),
1277    case file:rename(Tmp, Dat) of
1278	ok ->
1279	    swap_tmp_files(Tabs);
1280	_ -> 
1281	    file:delete(Tmp),
1282	    [Tab | swap_tmp_files(Tabs)]
1283    end;
1284swap_tmp_files([]) ->
1285    [].
1286
1287readable_indecies(Tab) ->
1288    val({Tab, index}).
1289
1290%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291%% Managing conditional debug functions
1292%%
1293%% The main idea with the debug_fun's is to allow test programs
1294%% to control the internal behaviour of Mnesia. This is needed
1295%% to make the test programs independent of system load, swapping
1296%% and other circumstances that may affect the behaviour of Mnesia.
1297%%
1298%% First should calls to ?eval_debug_fun be inserted at well
1299%% defined places in Mnesia's code. E.g. in critical situations
1300%% of startup, transaction commit, backups etc.
1301%%
1302%% Then compile Mnesia with the compiler option 'debug'.
1303%%
1304%% In test programs ?activate_debug_fun should be called
1305%% in order to bind a fun to the debug identifier stated
1306%% in the call to ?eval_debug_fun.
1307%%
1308%% If eval_debug_fun finds that the fun is activated it
1309%% invokes the fun as NewContext = Fun(PreviousContext, EvalContext)
1310%% and replaces the PreviousContext with the NewContext.
1311%% The initial context of a debug_fun is given as argument to
1312%% activate_debug_fun.
1313
1314-define(DEBUG_TAB, mnesia_debug).
1315-record(debug_info, {id, function, context, file, line}).
1316
1317scratch_debug_fun() ->
1318    dbg_out("scratch_debug_fun(): ~p~n", [?DEBUG_TAB]),
1319    (catch ?ets_delete_table(?DEBUG_TAB)),
1320    ?ets_new_table(?DEBUG_TAB, [set, public, named_table, {keypos, 2}]).
1321
1322activate_debug_fun(FunId, Fun, InitialContext, File, Line) ->
1323    Info = #debug_info{id = FunId,
1324		       function = Fun,
1325		       context = InitialContext,
1326		       file = File,
1327		       line = Line
1328		      },
1329    update_debug_info(Info).
1330
1331update_debug_info(Info) ->
1332    case catch ?ets_insert(?DEBUG_TAB, Info) of
1333	{'EXIT', _} ->
1334	    scratch_debug_fun(),
1335	    ?ets_insert(?DEBUG_TAB, Info);
1336	_ ->
1337	    ok
1338    end,
1339    dbg_out("update_debug_info(~p)~n", [Info]),
1340    ok.
1341
1342deactivate_debug_fun(FunId, _File, _Line) ->
1343    catch ?ets_delete(?DEBUG_TAB, FunId),
1344    ok.
1345
1346eval_debug_fun(FunId, EvalContext, EvalFile, EvalLine) ->
1347    case catch ?ets_lookup(?DEBUG_TAB, FunId) of
1348	[] ->
1349	    ok;
1350	[Info] ->
1351	    OldContext = Info#debug_info.context,
1352	    dbg_out("~s(~p): ~w "
1353		    "activated in ~s(~p)~n  "
1354		    "eval_debug_fun(~w, ~w)~n",
1355		    [filename:basename(EvalFile), EvalLine, Info#debug_info.id,
1356		     filename:basename(Info#debug_info.file), Info#debug_info.line,
1357		     OldContext, EvalContext]),
1358	    Fun = Info#debug_info.function,
1359	    NewContext = Fun(OldContext, EvalContext),
1360	    
1361	    case catch ?ets_lookup(?DEBUG_TAB, FunId) of
1362		[Info] when NewContext /= OldContext ->
1363		    NewInfo = Info#debug_info{context = NewContext},
1364		    update_debug_info(NewInfo);
1365		_ ->
1366		    ok
1367	    end;
1368	{'EXIT', _} -> ok    
1369    end.
1370	
1371-ifdef(debug).
1372    is_debug_compiled() -> true.
1373-else.
1374    is_debug_compiled() -> false.
1375-endif.   
1376
1377