aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2011-03-09 15:42:59 +0100
committerMicael Karlberg <[email protected]>2011-03-09 15:42:59 +0100
commitf8aac645c1c74626049b3a30eeedb6339cd43e2c (patch)
treefcda8e85564df7a84808c77e58e893d943a298a9
parent6c8f96a4a762e8bd9dd2ef758a5312791c064dd0 (diff)
parent62e056af8c4fa058faa5087614c6b837a07f06e6 (diff)
downloadotp-f8aac645c1c74626049b3a30eeedb6339cd43e2c.tar.gz
otp-f8aac645c1c74626049b3a30eeedb6339cd43e2c.tar.bz2
otp-f8aac645c1c74626049b3a30eeedb6339cd43e2c.zip
Merge branch 'dev' into bmk/snmp/support_ipv6_transport_address
-rw-r--r--lib/asn1/src/asn1ct_check.erl8
-rw-r--r--lib/asn1/test/asn1_SUITE.erl.src1
-rw-r--r--lib/asn1/test/asn1_SUITE_data/CAP.asn141
-rw-r--r--lib/edoc/src/edoc_lib.erl43
-rw-r--r--lib/edoc/src/edoc_wiki.erl3
-rwxr-xr-xlib/erl_docgen/priv/bin/xref_mod_app.escript4
-rw-r--r--lib/inets/doc/src/notes.xml83
-rw-r--r--lib/inets/src/http_lib/http_chunk.erl10
-rw-r--r--lib/inets/src/inets_app/inets.appup.src14
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/inviso/src/inviso_tool.erl6511
-rw-r--r--lib/inviso/src/inviso_tool_sh.erl3480
-rw-r--r--lib/inviso/test/inviso_tool_SUITE.erl36
-rw-r--r--lib/mnesia/src/mnesia.appup.src20
-rw-r--r--lib/mnesia/src/mnesia_frag.erl2
-rw-r--r--lib/mnesia/src/mnesia_schema.erl9
-rw-r--r--lib/mnesia/test/mnesia_SUITE.erl4
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/odbc/c_src/odbcserver.c4
-rw-r--r--lib/odbc/src/odbc.appup.src4
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/public_key/doc/src/public_key.xml4
-rw-r--r--lib/public_key/src/public_key.appup.src26
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/runtime_tools/src/inviso_rt.erl70
-rw-r--r--lib/snmp/doc/src/snmpc.xml2
-rw-r--r--lib/snmp/doc/src/snmpc_cmd.xml2
-rw-r--r--lib/ssl/doc/src/ssl.xml7
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl.erl11
-rw-r--r--lib/ssl/src/ssl_connection.erl88
-rw-r--r--lib/ssl/src/ssl_internal.hrl6
-rw-r--r--lib/ssl/test/Makefile3
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl44
-rw-r--r--lib/ssl/test/ssl_test_lib.erl12
-rw-r--r--lib/ssl/vsn.mk3
-rw-r--r--lib/tools/test/cover_SUITE.erl8
-rw-r--r--lib/typer/RELEASE_NOTES22
-rw-r--r--lib/typer/src/Makefile23
-rw-r--r--lib/typer/src/typer.app.src7
-rw-r--r--lib/typer/src/typer.erl995
-rw-r--r--lib/typer/src/typer.hrl64
-rw-r--r--lib/typer/src/typer_annotator.erl384
-rw-r--r--lib/typer/src/typer_info.erl162
-rw-r--r--lib/typer/src/typer_map.erl47
-rw-r--r--lib/typer/src/typer_options.erl191
-rw-r--r--lib/typer/src/typer_preprocess.erl154
-rw-r--r--lib/typer/vsn.mk2
48 files changed, 6357 insertions, 6267 deletions
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 8b1ee6e601..efd731f052 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -6176,7 +6176,6 @@ componentrelation_leadingattr(_,[],_CompList,[],NewCompList) ->
{false,lists:reverse(NewCompList)};
componentrelation_leadingattr(_,[],_CompList,LeadingAttr,NewCompList) ->
{lists:last(LeadingAttr),lists:reverse(NewCompList)}; %send all info in Ts later
-
componentrelation_leadingattr(S,[C= #'ComponentType'{}|Cs],CompList,Acc,CompAcc) ->
{LAAcc,NewC} =
case catch componentrelation1(S,C#'ComponentType'.typespec,
@@ -6229,7 +6228,10 @@ componentrelation_leadingattr(S,[C= #'ComponentType'{}|Cs],CompList,Acc,CompAcc)
{[],C}
end,
componentrelation_leadingattr(S,Cs,CompList,LAAcc++Acc,
- [NewC|CompAcc]).
+ [NewC|CompAcc]);
+componentrelation_leadingattr(S,[NotComponentType|Cs],CompList,LeadingAttr,NewCompList) ->
+ componentrelation_leadingattr(S,Cs,CompList,LeadingAttr,[NotComponentType|NewCompList]).
+
object_set_mod_name(_S,ObjSet) when is_atom(ObjSet) ->
ObjSet;
diff --git a/lib/asn1/test/asn1_SUITE.erl.src b/lib/asn1/test/asn1_SUITE.erl.src
index e1a09adc82..fd0bae34c8 100644
--- a/lib/asn1/test/asn1_SUITE.erl.src
+++ b/lib/asn1/test/asn1_SUITE.erl.src
@@ -2327,6 +2327,7 @@ ber_modules() ->
test_modules() ->
_Modules = [
"BitStr",
+ "CAP",
"CommonDataTypes",
"Constraints",
"ContextSwitchingTypes",
diff --git a/lib/asn1/test/asn1_SUITE_data/CAP.asn1 b/lib/asn1/test/asn1_SUITE_data/CAP.asn1
new file mode 100644
index 0000000000..69d8486d3b
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/CAP.asn1
@@ -0,0 +1,41 @@
+CAP {ccitt(0) identified-organization(4) etsi(0) mobileDomain(0) umts-network(1) modules(3) cap-datatypes(52) version3(2)}
+
+DEFINITIONS IMPLICIT TAGS ::=
+
+BEGIN
+
+EXTENSION ::= CLASS {
+ &ExtensionType,
+ &criticality CriticalityType DEFAULT ignore,
+ &id Code
+ }
+WITH SYNTAX {
+ EXTENSION-SYNTAX &ExtensionType
+ CRITICALITY &criticality
+ IDENTIFIED BY &id
+ }
+
+ExtensionField ::= SEQUENCE {
+ type EXTENSION.&id ({SupportedExtensions }),
+ criticality CriticalityType DEFAULT ignore,
+ value [1] EXTENSION.&ExtensionType ({SupportedExtensions }{@type}),
+ ...}
+
+SupportedExtensions EXTENSION ::= {firstExtension, ...}
+
+firstExtension EXTENSION ::= {
+ EXTENSION-SYNTAX NULL
+ CRITICALITY ignore
+ IDENTIFIED BY global : {itu-t(0) identified-organization(4) organisation(0) gsm(1)
+ capextension(2)}}
+
+CriticalityType ::= ENUMERATED {
+ ignore (0),
+ abort (1)
+ }
+
+Code ::= CHOICE {local INTEGER,
+ global OBJECT IDENTIFIER}
+
+
+END
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index c1f95a7a67..6705ccd356 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -16,7 +16,6 @@
%%
%% $Id$
%%
-%% @private
%% @copyright 2001-2003 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @see edoc
@@ -49,14 +48,17 @@
%% ---------------------------------------------------------------------
%% List and string utilities
+%% @private
timestr({H,M,Sec}) ->
lists:flatten(io_lib:fwrite("~2.2.0w:~2.2.0w:~2.2.0w",[H,M,Sec])).
+%% @private
datestr({Y,M,D}) ->
Ms = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"],
lists:flatten(io_lib:fwrite("~s ~w ~w",[lists:nth(M, Ms),D,Y])).
+%% @private
count(X, Xs) ->
count(X, Xs, 0).
@@ -67,6 +69,7 @@ count(X, [_ | Xs], N) ->
count(_X, [], N) ->
N.
+%% @private
lines(Cs) ->
lines(Cs, [], []).
@@ -77,6 +80,7 @@ lines([C | Cs], As, Ls) ->
lines([], As, Ls) ->
lists:reverse([lists:reverse(As) | Ls]).
+%% @private
split_at(Cs, K) ->
split_at(Cs, K, []).
@@ -87,6 +91,7 @@ split_at([C | Cs], K, As) ->
split_at([], _K, As) ->
{lists:reverse(As), []}.
+%% @private
split_at_stop(Cs) ->
split_at_stop(Cs, []).
@@ -103,6 +108,7 @@ split_at_stop([C | Cs], As) ->
split_at_stop([], As) ->
{lists:reverse(As), []}.
+%% @private
split_at_space(Cs) ->
split_at_space(Cs, []).
@@ -117,17 +123,20 @@ split_at_space([C | Cs], As) ->
split_at_space([], As) ->
{lists:reverse(As), []}.
+%% @private
is_space([$\s | Cs]) -> is_space(Cs);
is_space([$\t | Cs]) -> is_space(Cs);
is_space([$\n | Cs]) -> is_space(Cs);
is_space([_C | _Cs]) -> false;
is_space([]) -> true.
+%% @private
strip_space([$\s | Cs]) -> strip_space(Cs);
strip_space([$\t | Cs]) -> strip_space(Cs);
strip_space([$\n | Cs]) -> strip_space(Cs);
strip_space(Cs) -> Cs.
+%% @private
segment(Es, N) ->
segment(Es, [], [], 0, N).
@@ -140,6 +149,7 @@ segment([], [], Cs, _N, _M) ->
segment([], As, Cs, _N, _M) ->
lists:reverse([lists:reverse(As) | Cs]).
+%% @private
transpose([]) -> [];
transpose([[] | Xss]) -> transpose(Xss);
transpose([[X | Xs] | Xss]) ->
@@ -151,6 +161,7 @@ transpose([[X | Xs] | Xss]) ->
%% end of the summary sentence only if it is also the last segment in
%% the list, or is followed by a 'p' or 'br' ("whitespace") element.
+%% @private
get_first_sentence([#xmlElement{name = p, content = Es} | _]) ->
%% Descend into initial paragraph.
get_first_sentence_1(Es);
@@ -230,6 +241,7 @@ end_of_sentence_1(_, false, _) ->
%% Names must begin with a lowercase letter and contain only
%% alphanumerics and underscores.
+%% @private
is_name([C | Cs]) when C >= $a, C =< $z ->
is_name_1(Cs);
is_name([C | Cs]) when C >= $\337, C =< $\377, C =/= $\367 ->
@@ -252,6 +264,7 @@ is_name_1(_) -> false.
to_atom(A) when is_atom(A) -> A;
to_atom(S) when is_list(S) -> list_to_atom(S).
+%% @private
unique([X | Xs]) -> [X | unique(Xs, X)];
unique([]) -> [].
@@ -267,6 +280,7 @@ unique([], _) -> [].
%% content of <a href="overview-summary.html#ftag-equiv">`@equiv'</a>
%% tags, and strings denoting file names, e.g. in @headerfile. Also used
%% by {@link edoc_run}.
+%% @private
parse_expr(S, L) ->
case erl_scan:string(S ++ ".", L) of
@@ -287,10 +301,11 @@ parse_expr(S, L) ->
%% @doc EDoc "contact information" parsing. This is the type of the
%% content in e.g.
%% <a href="overview-summary.html#mtag-author">`@author'</a> tags.
+%% @private
-%% @type info() = #info{name = string(),
-%% email = string(),
-%% uri = string()}
+%% % @type info() = #info{name = string(),
+%% % email = string(),
+%% % uri = string()}
-record(info, {name = "" :: string(),
email = "" :: string(),
@@ -367,6 +382,7 @@ strip_and_reverse(As) ->
%%
%% TODO: general utf-8 encoding for all of Unicode (0-16#10ffff)
+%% @private
escape_uri([C | Cs]) when C >= $a, C =< $z ->
[C | escape_uri(Cs)];
escape_uri([C | Cs]) when C >= $A, C =< $Z ->
@@ -409,6 +425,7 @@ hex_octet(N) ->
%% Please note that URI are *not* file names. Don't use the stdlib
%% 'filename' module for operations on (any parts of) URI.
+%% @private
join_uri(Base, "") ->
Base;
join_uri("", Path) ->
@@ -418,6 +435,7 @@ join_uri(Base, Path) ->
%% Check for relative URI; "network paths" ("//...") not included!
+%% @private
is_relative_uri([$: | _]) ->
false;
is_relative_uri([$/, $/ | _]) ->
@@ -433,6 +451,7 @@ is_relative_uri([_ | Cs]) ->
is_relative_uri([]) ->
true.
+%% @private
uri_get("file:///" ++ Path) ->
uri_get_file(Path);
uri_get("file://localhost/" ++ Path) ->
@@ -532,6 +551,7 @@ uri_get_ftp(URI) ->
Msg = io_lib:format("cannot access ftp scheme yet: '~s'.", [URI]),
{error, Msg}.
+%% @private
to_label([$\s | Cs]) ->
to_label(Cs);
to_label([$\t | Cs]) ->
@@ -564,6 +584,7 @@ to_label_2(Cs) ->
%% ---------------------------------------------------------------------
%% Files
+%% @private
filename([C | T]) when is_integer(C), C > 0 ->
[C | filename(T)];
filename([H|T]) ->
@@ -576,6 +597,7 @@ filename(N) ->
report("bad filename: `~P'.", [N, 25]),
exit(error).
+%% @private
copy_file(From, To) ->
case file:copy(From, To) of
{ok, _} -> ok;
@@ -600,6 +622,7 @@ list_dir(Dir, Error) ->
F("could not read directory '~s': ~s.", [filename(Dir), R1])
end.
+%% @private
simplify_path(P) ->
case filename:basename(P) of
"." ->
@@ -636,6 +659,7 @@ simplify_path(P) ->
%% exit(error)
%% end.
+%% @private
try_subdir(Dir, Subdir) ->
D = filename:join(Dir, Subdir),
case filelib:is_dir(D) of
@@ -648,6 +672,7 @@ try_subdir(Dir, Subdir) ->
%%
%% @doc Write the given `Text' to the file named by `Name' in directory
%% `Dir'. If the target directory does not exist, it will be created.
+%% @private
write_file(Text, Dir, Name) ->
write_file(Text, Dir, Name, '').
@@ -657,6 +682,7 @@ write_file(Text, Dir, Name) ->
%% Name::edoc:filename(), Package::atom()|string()) -> ok
%% @doc Like {@link write_file/3}, but adds path components to the target
%% directory corresponding to the specified package.
+%% @private
write_file(Text, Dir, Name, Package) ->
Dir1 = filename:join([Dir | packages:split(Package)]),
@@ -672,6 +698,7 @@ write_file(Text, Dir, Name, Package) ->
exit(error)
end.
+%% @private
write_info_file(App, Packages, Modules, Dir) ->
Ts = [{packages, Packages},
{modules, Modules}],
@@ -703,6 +730,7 @@ info_file_data(Ts) ->
%% Local file access - don't complain if file does not exist.
+%% @private
read_info_file(Dir) ->
File = filename:join(Dir, ?INFO_FILE),
case filelib:is_file(File) of
@@ -769,11 +797,13 @@ parse_terms_1([], _As, _Vs) ->
%% ---------------------------------------------------------------------
%% Source files and packages
+%% @private
find_sources(Path, Opts) ->
find_sources(Path, "", Opts).
%% @doc See {@link edoc:run/3} for a description of the options
%% `subpackages', `source_suffix' and `exclude_packages'.
+%% @private
%% NEW-OPTIONS: subpackages, source_suffix, exclude_packages
%% DEFER-OPTIONS: edoc:run/3
@@ -827,6 +857,7 @@ is_package_dir(Name, Dir) ->
is_name(filename:rootname(filename:basename(Name)))
andalso filelib:is_dir(filename:join(Dir, Name)).
+%% @private
find_file([P | Ps], Pkg, Name) ->
Dir = filename:join(P, filename:join(packages:split(Pkg))),
File = filename:join(Dir, Name),
@@ -839,6 +870,7 @@ find_file([P | Ps], Pkg, Name) ->
find_file([], _Pkg, _Name) ->
"".
+%% @private
find_doc_dirs() ->
find_doc_dirs(code:get_path()).
@@ -904,6 +936,7 @@ add_new(K, V, D) ->
%% @spec (Options::proplist()) -> edoc_env()
%% @equiv get_doc_env([], [], [], Opts)
+%% @private
get_doc_env(Opts) ->
get_doc_env([], [], [], Opts).
@@ -952,6 +985,7 @@ get_doc_env(App, Packages, Modules, Opts) ->
%% NEW-OPTIONS: doclet
%% DEFER-OPTIONS: edoc:run/3
+%% @private
run_doclet(Fun, Opts) ->
run_plugin(doclet, ?DEFAULT_DOCLET, Fun, Opts).
@@ -961,6 +995,7 @@ run_doclet(Fun, Opts) ->
%% NEW-OPTIONS: layout
%% DEFER-OPTIONS: edoc:layout/2
+%% @private
run_layout(Fun, Opts) ->
run_plugin(layout, ?DEFAULT_LAYOUT, Fun, Opts).
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
index e4a3d74734..b36aaae6ce 100644
--- a/lib/edoc/src/edoc_wiki.erl
+++ b/lib/edoc/src/edoc_wiki.erl
@@ -82,7 +82,8 @@ parse_xml(Data, Line) ->
parse_xml_1(Text, Line) ->
Text1 = "<doc>" ++ Text ++ "</doc>",
- case catch {ok, xmerl_scan:string(Text1, [{line, Line}])} of
+ Options = [{line, Line}, {encoding, "iso-8859-1"}],
+ case catch {ok, xmerl_scan:string(Text1, Options)} of
{ok, {E, _}} ->
E#xmlElement.content;
{'EXIT', {fatal, {Reason, L, _C}}} ->
diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript
index fcc3a96ada..13671ef2f8 100755
--- a/lib/erl_docgen/priv/bin/xref_mod_app.escript
+++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript
@@ -2,7 +2,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -86,8 +86,6 @@ appmods(D) ->
end,
{App, [filename:basename(EF, ".erl") || EF <- ErlFiles]}.
--include_lib("xmerl/include/xmerl.hrl").
-
-define(IND(N), lists:duplicate(N, $\s)).
-define(NL, "\n").
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 11b0af4310..5da9d98002 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2002</year><year>2010</year>
+ <year>2002</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,50 +32,78 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.5.1</title>
+ <section><title>Inets 5.5.2</title>
- <section><title>Fixed Bugs and Malfunctions</title>
+ <section><title>Improvements and New Features</title>
+ <p>-</p>
+
+<!--
<list>
<item>
- <p> Fix format_man_pages so it handles all man sections
- and remove warnings/errors in various man pages. </p>
<p>
- Own Id: OTP-8600</p>
+ Miscellaneous inet6 related problems.</p>
+ <p>Own Id: OTP-8927</p>
</item>
+ </list>
+-->
+
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
<item>
- <p>
- [httpc] Pipelined and queued requests not processed when
- connection closed remotelly.</p>
- <p>
- Own Id: OTP-8906</p>
+ <p>[httpd] httpd_response:send_chunk handles empty list and
+ empty binary - i.e. no chunk is sent, but it does
+ not handle a list with an empty binary [&lt;&lt;&gt;&gt;].
+ This will be sent as an empty chunk - which in turn
+ will be encoded by http_chunk to the same as a final
+ chunk, which will make the http client believe that
+ the end of the page is reached.</p>
+ <p>Own Id: OTP-8906</p>
</item>
</list>
</section>
+ </section> <!-- 5.5.2 -->
+
+
+ <section><title>Inets 5.5.1</title>
<section><title>Improvements and New Features</title>
<list>
<item>
- <p>
- Miscellaneous inet6 related problems.</p>
- <p>
- Own Id: OTP-8927</p>
+ <p>Miscellaneous inet6 related problems.</p>
+ <p>Own Id: OTP-8927</p>
</item>
<item>
- <p>
- Updated http-server to make sure URLs in error-messages
- are URL-encoded. Added support in http-client to use
- URL-encoding. Also added the missing include directory
- for the inets application.</p>
- <p>
- Own Id: OTP-8940 Aux Id: seq11735 </p>
+ <p>Updated http-server to make sure URLs in error-messages
+ are URL-encoded. Added support in http-client to use
+ URL-encoding. Also added the missing include directory
+ for the inets application.</p>
+ <p>Own Id: OTP-8940 Aux Id: seq11735 </p>
</item>
</list>
</section>
-</section>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fix format_man_pages so it handles all man sections
+ and remove warnings/errors in various man pages. </p>
+ <p>Own Id: OTP-8600</p>
+ </item>
+ <item>
+ <p>[httpc] Pipelined and queued requests not processed when
+ connection closed remotelly.</p>
+ <p>Own Id: OTP-8906</p>
+ </item>
+ </list>
+ </section>
-<section><title>Inets 5.5</title>
+ </section> <!-- 5.5.1 -->
+
+
+ <section><title>Inets 5.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -120,9 +148,10 @@
</list>
</section>
-</section>
+ </section> <!-- 5.5 -->
+
-<section><title>Inets 5.4</title>
+ <section><title>Inets 5.4</title>
<section><title>Improvements and New Features</title>
<!--
diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl
index 621bc68eae..57647438e9 100644
--- a/lib/inets/src/http_lib/http_chunk.erl
+++ b/lib/inets/src/http_lib/http_chunk.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,8 @@
%% %CopyrightEnd%
%%
%% Description: Implements chunked transfer encoding see RFC2616 section
-%% 3.6.1
+%% 3.6.1
+
-module(http_chunk).
-include("http_internal.hrl").
@@ -28,6 +29,7 @@
%% little at a time on a socket.
-export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -81,6 +83,9 @@ encode(Chunk) when is_binary(Chunk)->
HEXSize = list_to_binary(http_util:integer_to_hexlist(size(Chunk))),
<<HEXSize/binary, ?CR, ?LF, Chunk/binary, ?CR, ?LF>>;
+encode([<<>>]) ->
+ [];
+
encode(Chunk) when is_list(Chunk)->
HEXSize = http_util:integer_to_hexlist(erlang:iolist_size(Chunk)),
[HEXSize, ?CR, ?LF, Chunk, ?CR, ?LF].
@@ -88,6 +93,7 @@ encode(Chunk) when is_list(Chunk)->
encode_last() ->
<<$0, ?CR, ?LF, ?CR, ?LF >>.
+
%%-------------------------------------------------------------------------
%% handle_headers(HeaderRecord, ChunkedHeaders) -> NewHeaderRecord
%%
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 0194c65db9..07da8ca961 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,7 +1,7 @@
%% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,11 @@
{"%VSN%",
[
+ {"5.5.1",
+ [
+ {load_module, http_chunk, soft_purge, soft_purge, []}
+ ]
+ },
{"5.5",
[
{restart_application, inets}
@@ -30,7 +35,12 @@
}
],
[
- {"5.4",
+ {"5.5.1",
+ [
+ {load_module, http_chunk, soft_purge, soft_purge, []}
+ ]
+ },
+ {"5.5",
[
{restart_application, inets}
]
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 67737ee552..b1de3fef43 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+# Copyright Ericsson AB 2001-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inviso/src/inviso_tool.erl b/lib/inviso/src/inviso_tool.erl
index 05158f58fe..7d3cfb9da0 100644
--- a/lib/inviso/src/inviso_tool.erl
+++ b/lib/inviso/src/inviso_tool.erl
@@ -1,3324 +1,3255 @@
-% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
-%%
-%% Description:
-%% The inviso_tool implementation. A tool that uses inviso.
-%%
-%% Authors:
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool).
-
-
-%% This is the inviso tool, which is a tool using the inviso trace application.
-%% It is developed to make tracing using trace cases possible in an environment
-%% of distributed Erlang nodes.
-%% A current restriction is that the Erlang nodes are supposed to have the same
-%% code. This since inviso tool can at this point not handle subsets of nodes.
-%% Instead all participating Erlang nodes are treated the same.
-%%
-%% The main functionality of the inviso tool are:
-%%
-%% (1) Handles start and stop of tracing at participating nodes.
-%% (2) Interprets trace-case files at a distributed network level.
-%% (The inviso runtime component is responsible for interpreting
-%% trace cases at a local level, if run in an autostart).
-%% (3) Keeps a command history log from which:
-%% (a) Sequences easily can be repeated.
-%% (b) Autostart configuration files can be created (understood by the
-%% default inviso autostart mechanism).
-%% (4) Performs reactivation in case tracing is suspended (manually or by
-%% an overload mechanism).
-%% (5) Can reconnect crashed nodes and by using the history bringing them
-%% up to speed.
-
-%% Distributed Erlang
-%% ------------------
-%% Inviso is built to run in a distributed environment.
-%% The inviso tool can also be used in a non distributed environment.
-
-%% Short description
-%% -----------------
-%% Start-up of the inviso tool
-%% During the start-up of the tool, the tool starts runtime components at
-%% all participating nodes. A runtime component can already be running at
-%% a particular node and will then simply be adopted.
-%%
-%% Session
-%% A session is said to start when tracing is initiated, and ends when
-%% made to stop by the user. When a session is stopped, tracing is stopped
-%% at all participating nodes. Note that participating nodes may come and
-%% go though the time-frame of a session. That means that if a node is
-%% reconnected it may resume its tracing in the current session through
-%% a 'restart_session'. A runtime component that is already tracing at the
-%% time start-session will simply be part of the session without its
-%% ingoing tracing being changed.
-%%
-%% Reactivation
-%% A node that is suspended can be reactivated to resume tracing. Note that
-%% tracing has in this situation never been stopped at the node in question.
-%% The inviso tool resumes the node and applies the history to it.
-%%
-%% Reconnect
-%% A node that is "down" from the inviso tool's perspective can be
-%% reconnected. During reconnection the tool restarts the runtime component
-%% at that node but does not (re)initiate tracing. The latter is called
-%% restart_session and must be done explicitly, unless the node in question
-%% is in fact already tracing. If the node is already tracing (due to an autostart
-%% for instance), it automatically becomes part of the ongoing session (if
-%% there is an ongoing session).
-%%
-%% Restart Session
-%% A node that has been down and has been reconnected can be made to
-%% initialize and resume its tracing. This is done by starting the session
-%% at the node in question and redoing the current history.
-
-%% Trace files within a session
-%% Since it is possible to init-tracing (from an inviso perspective) several
-%% times within the same session, a session may leave several trace log files
-%% behind. This must be resolved by the tracer data generator function
-%% (user supplied) by marking filenames in a chronological order but still
-%% making them possible to identify as part of the same session
-
-
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/0,start/1,stop/0,stop/1]).
--export([reconnect_nodes/0,reconnect_nodes/1,
- start_session/0,start_session/1,
- reinitiate_session/0,reinitiate_session/1,
- restore_session/0,restore_session/1,restore_session/2,
- stop_session/0,
- reset_nodes/0,reset_nodes/1,
- atc/3,sync_atc/3,sync_atc/4,
- sync_rtc/2,sync_rtc/3,
- dtc/2,sync_dtc/2,sync_dtc/3,
- inviso/2]).
--export([reactivate/0,reactivate/1,
- save_history/1,
- get_autostart_data/1,get_autostart_data/2,
- get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
--export([flush/0,flush/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Debug exports.
-%% -----------------------------------------------------------------------------
-
--export([get_loopdata/0]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% OTP exports and call backs.
-%% -----------------------------------------------------------------------------
-
--export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([tc_executer/4,reactivator_executer/6]).
--export([std_options_generator/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
-%% Defines the inviso function calls that shall be possible to do through the
-%% inviso API in this tool.
--define(INVISO_CMDS,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0},
- {suspend,1},{cancel_suspension,0}]).
-%% -----------------------------------------------------------------------------
-
-%% These inviso functions shall be included in the command history log. Others
-%% are not relevant to be redone during a recactivation, a restart session or
-%% exported to an autostart file.
--define(INVISO_CMD_HISTORY,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0}]).
-%% -----------------------------------------------------------------------------
-
-%% Since many function calls to inviso may take long time, especially if they
-%% involve difficult and many trace patterns to set, the default gen_server:call
-%% time out can not be used. We just do not want to get stuck for ever if some
-%% error occurs.
--define(CALL_TIMEOUT,60000).
-
-%% Default max time to wait for a trace case called synchronously to return.
--define(SYNC_TC_TIMEOUT,10000).
-
-%% Runtime components shall terminate when the tool terminates.
--define(DEFAULT_DEPENDENCY,{dependency,0}).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definitions.
-%% -----------------------------------------------------------------------------
-
-%% The loopdata record.
--record(ld,{
- dir=".", % Working dir of the tool.
- nodes=down, % The nodesD database, defaults to non-distr.
- c_node, % Location of inviso_c.
- c_pid, % The inviso control component.
- regexp_node, % Node for regexp expansions.
- tc_dict, % Trace case definition db.
- chl, % Command history log.
- session_state=passive, % passive | tracing
- tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
- tracer_data, % Current session nr and TDGargs.
- reactivators=[], % Pids of now running reactivators.
- tc_def_file, % Trace case definition file.
- optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
- initial_tcs=[], % Initial trace cases.
- started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
- history_dir, % File path for history file.
- keep_nodes=[], % Nodes that shall not be cleared when stopping.
- debug=false % Internal debug mode
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API
-%% =============================================================================
-
-%% start()={ok,Pid} | {error,{already_started,pid()}}
-%% start(Config)
-%% Config=[{Opt,Value},...], list of tuple options.
-%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
-%% history_dir|keep_nodes
-%% Starts the inviso_tool process. Options in Config are the same as those
-%% which are kept in the #ld structure.
-start() ->
- start([]).
-start(Config) ->
- gen_server:start({local,?MODULE},?MODULE,Config,[]).
-%% -----------------------------------------------------------------------------
-
-%% stop(UntouchedNodes)=
-%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
-%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason} | patterns_untouched
-%% Stops the inviso tool and the inviso control component. Runtime components are
-%% stopped by them selves depending on their dependcy of the control component.
-%% All runtime components that are not marked as to be kept will have their
-%% trace patterns cleared before the inviso control component is shutdown.
-%% The NodeResults indicates which nodes were successfullt handled.
-stop() ->
- stop([]).
-stop(UntouchedNodes) ->
- gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
-%% reconnect_nodes(Nodes)={ok,NodesResults}
-%% NodesResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}} | {error,NReason}
-%% State=tracing | inactive
-%% Status=running | suspended
-%% NReason=unknown_node | already_connected | down
-%% (Re)starts the inviso runtime components at Nodes. Depending on its state
-%% (new,idle or tracing) and if the tool is running a session or not, it becomes
-%% part of the tool's ongoing session. If the newly reconnected node is not
-%% tracing but the tool runs a session, the node must be reinitiated to become
-%% tracing.
-reconnect_nodes() ->
- gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Description:
+%% The inviso_tool implementation. A tool that uses inviso.
+%%
+%% Authors:
+%% Lennart Öhman, [email protected]
+%% -----------------------------------------------------------------------------
+
+-module(inviso_tool).
+
+
+%% This is the inviso tool, which is a tool using the inviso trace application.
+%% It is developed to make tracing using trace cases possible in an environment
+%% of distributed Erlang nodes.
+%% A current restriction is that the Erlang nodes are supposed to have the same
+%% code. This since inviso tool can at this point not handle subsets of nodes.
+%% Instead all participating Erlang nodes are treated the same.
+%%
+%% The main functionality of the inviso tool are:
+%%
+%% (1) Handles start and stop of tracing at participating nodes.
+%% (2) Interprets trace-case files at a distributed network level.
+%% (The inviso runtime component is responsible for interpreting
+%% trace cases at a local level, if run in an autostart).
+%% (3) Keeps a command history log from which:
+%% (a) Sequences easily can be repeated.
+%% (b) Autostart configuration files can be created (understood by the
+%% default inviso autostart mechanism).
+%% (4) Performs reactivation in case tracing is suspended (manually or by
+%% an overload mechanism).
+%% (5) Can reconnect crashed nodes and by using the history bringing them
+%% up to speed.
+
+%% Distributed Erlang
+%% ------------------
+%% Inviso is built to run in a distributed environment.
+%% The inviso tool can also be used in a non distributed environment.
+
+%% Short description
+%% -----------------
+%% Start-up of the inviso tool
+%% During the start-up of the tool, the tool starts runtime components at
+%% all participating nodes. A runtime component can already be running at
+%% a particular node and will then simply be adopted.
+%%
+%% Session
+%% A session is said to start when tracing is initiated, and ends when
+%% made to stop by the user. When a session is stopped, tracing is stopped
+%% at all participating nodes. Note that participating nodes may come and
+%% go though the time-frame of a session. That means that if a node is
+%% reconnected it may resume its tracing in the current session through
+%% a 'restart_session'. A runtime component that is already tracing at the
+%% time start-session will simply be part of the session without its
+%% ingoing tracing being changed.
+%%
+%% Reactivation
+%% A node that is suspended can be reactivated to resume tracing. Note that
+%% tracing has in this situation never been stopped at the node in question.
+%% The inviso tool resumes the node and applies the history to it.
+%%
+%% Reconnect
+%% A node that is "down" from the inviso tool's perspective can be
+%% reconnected. During reconnection the tool restarts the runtime component
+%% at that node but does not (re)initiate tracing. The latter is called
+%% restart_session and must be done explicitly, unless the node in question
+%% is in fact already tracing. If the node is already tracing (due to an autostart
+%% for instance), it automatically becomes part of the ongoing session (if
+%% there is an ongoing session).
+%%
+%% Restart Session
+%% A node that has been down and has been reconnected can be made to
+%% initialize and resume its tracing. This is done by starting the session
+%% at the node in question and redoing the current history.
+
+%% Trace files within a session
+%% Since it is possible to init-tracing (from an inviso perspective) several
+%% times within the same session, a session may leave several trace log files
+%% behind. This must be resolved by the tracer data generator function
+%% (user supplied) by marking filenames in a chronological order but still
+%% making them possible to identify as part of the same session
+
+
+
+%% -----------------------------------------------------------------------------
+%% API exports.
+%% -----------------------------------------------------------------------------
+
+-export([start/0,start/1,stop/0,stop/1]).
+-export([reconnect_nodes/0,reconnect_nodes/1,
+ start_session/0,start_session/1,
+ reinitiate_session/0,reinitiate_session/1,
+ restore_session/0,restore_session/1,restore_session/2,
+ stop_session/0,
+ reset_nodes/0,reset_nodes/1,
+ atc/3,sync_atc/3,sync_atc/4,
+ sync_rtc/2,sync_rtc/3,
+ dtc/2,sync_dtc/2,sync_dtc/3,
+ inviso/2]).
+-export([reactivate/0,reactivate/1,
+ save_history/1,
+ get_autostart_data/1,get_autostart_data/2,
+ get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
+-export([flush/0,flush/1]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Debug exports.
+%% -----------------------------------------------------------------------------
+
+-export([get_loopdata/0]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% OTP exports and call backs.
+%% -----------------------------------------------------------------------------
+
+-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Internal exports.
+%% -----------------------------------------------------------------------------
+
+-export([tc_executer/4,reactivator_executer/6]).
+-export([std_options_generator/1]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Constants.
+%% -----------------------------------------------------------------------------
+
+%% Defines the inviso function calls that shall be possible to do through the
+%% inviso API in this tool.
+-define(INVISO_CMDS,
+ [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
+ {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
+ {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
+ {init_tpm,4},{init_tpm,7},
+ {tpm,4},{tpm,5},{tpm,8},
+ {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
+ {tpm_ms,5},{tpm_ms_tracer,5},
+ {ctpm_ms,4},{ctpm,3},
+ {tpm_localnames,0},{ctpm_localnames,0},
+ {tpm_globalnames,0},{ctpm_globalnames,0},
+ {ctp_all,0},
+ {suspend,1},{cancel_suspension,0}]).
+%% -----------------------------------------------------------------------------
+
+%% These inviso functions shall be included in the command history log. Others
+%% are not relevant to be redone during a recactivation, a restart session or
+%% exported to an autostart file.
+-define(INVISO_CMD_HISTORY,
+ [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
+ {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
+ {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
+ {init_tpm,4},{init_tpm,7},
+ {tpm,4},{tpm,5},{tpm,8},
+ {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
+ {tpm_ms,5},{tpm_ms_tracer,5},
+ {ctpm_ms,4},{ctpm,3},
+ {tpm_localnames,0},{ctpm_localnames,0},
+ {tpm_globalnames,0},{ctpm_globalnames,0},
+ {ctp_all,0}]).
+%% -----------------------------------------------------------------------------
+
+%% Since many function calls to inviso may take long time, especially if they
+%% involve difficult and many trace patterns to set, the default gen_server:call
+%% time out can not be used. We just do not want to get stuck for ever if some
+%% error occurs.
+-define(CALL_TIMEOUT,60000).
+
+%% Default max time to wait for a trace case called synchronously to return.
+-define(SYNC_TC_TIMEOUT,10000).
+
+%% Runtime components shall terminate when the tool terminates.
+-define(DEFAULT_DEPENDENCY,{dependency,0}).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Record definitions.
+%% -----------------------------------------------------------------------------
+
+%% The loopdata record.
+-record(ld,{
+ dir=".", % Working dir of the tool.
+ nodes=down, % The nodesD database, defaults to non-distr.
+ c_node, % Location of inviso_c.
+ c_pid, % The inviso control component.
+ regexp_node, % Node for regexp expansions.
+ tc_dict, % Trace case definition db.
+ chl, % Command history log.
+ session_state=passive, % passive | tracing
+ tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
+ tracer_data, % Current session nr and TDGargs.
+ reactivators=[], % Pids of now running reactivators.
+ tc_def_file, % Trace case definition file.
+ optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
+ initial_tcs=[], % Initial trace cases.
+ started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
+ history_dir, % File path for history file.
+ keep_nodes=[], % Nodes that shall not be cleared when stopping.
+ debug=false % Internal debug mode
+ }).
+%% -----------------------------------------------------------------------------
+
+
+%% =============================================================================
+%% API
+%% =============================================================================
+
+%% start()={ok,Pid} | {error,{already_started,pid()}}
+%% start(Config)
+%% Config=[{Opt,Value},...], list of tuple options.
+%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
+%% history_dir|keep_nodes
+%% Starts the inviso_tool process. Options in Config are the same as those
+%% which are kept in the #ld structure.
+start() ->
+ start([]).
+start(Config) ->
+ gen_server:start({local,?MODULE},?MODULE,Config,[]).
+%% -----------------------------------------------------------------------------
+
+%% stop(UntouchedNodes)=
+%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
+%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
+%% NodeResults=[{Node,NodeResult},...]
+%% NodeResult=ok | {error,Reason} | patterns_untouched
+%% Stops the inviso tool and the inviso control component. Runtime components are
+%% stopped by them selves depending on their dependcy of the control component.
+%% All runtime components that are not marked as to be kept will have their
+%% trace patterns cleared before the inviso control component is shutdown.
+%% The NodeResults indicates which nodes were successfullt handled.
+stop() ->
+ stop([]).
+stop(UntouchedNodes) ->
+ gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
+%% reconnect_nodes(Nodes)={ok,NodesResults}
+%% NodesResults=[{Node,NodeResult},...]
+%% NodeResult={ok,{State,Status}} | {error,NReason}
+%% State=tracing | inactive
+%% Status=running | suspended
+%% NReason=unknown_node | already_connected | down
+%% (Re)starts the inviso runtime components at Nodes. Depending on its state
+%% (new,idle or tracing) and if the tool is running a session or not, it becomes
+%% part of the tool's ongoing session. If the newly reconnected node is not
+%% tracing but the tool runs a session, the node must be reinitiated to become
+%% tracing.
+reconnect_nodes() ->
+ gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
reconnect_nodes(Node) when is_atom(Node) ->
- reconnect_nodes([Node]);
+ reconnect_nodes([Node]);
reconnect_nodes(Nodes) when is_list(Nodes) ->
- gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% start_session(MoreTDGargs)=
-%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
-%% tracer data generator function.
-%% SessionNr=integer(), trace sessions are numbered by the tool.
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
-%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
-%% Initiates tracing at all participating nodes.
-start_session() ->
- start_session([]).
-start_session(MoreTDGargs) ->
- gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
-%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
-%% of the tool or not in state inactive will be marked as failing by the
-%% tool in the InvisoReturn.
-%% To reinitate a node means to (inviso) init tracing at it according to saved
-%% tracer data generator arguments for the current session and then redo the current
-%% history to bring it up to speed. Note that the tool must be running a session
-%% for reinitiate to work.
-reinitiate_session() ->
- gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
-reinitiate_session(Nodes) ->
- gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% restore_session()=
-%% restore_session(MoreTDGargs)=
-%% restore_session(FileName)=
-%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% The two first clauses will start a new session using the last history. This
-%% implies that there must have been a session running prior.
-%% The two last clauses starts a session and reads a history file and executes the
-%% tracecases in it at all inactive nodes.
-%% In both cases the reused or read history becomes the current histoy, just if the
-%% session had been initiated manually. The tool may not
-%% have a session ongoing, and nodes already tracing (nodes which were adopted)
-%% are not effected. Just like when starting a session manually.
-restore_session() ->
- restore_session([]).
-restore_session([]) -> % This cant be a filename.
- gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
-restore_session(FileNameOrMoreTDGargs) ->
- case is_string(FileNameOrMoreTDGargs) of
- true -> % Interpret it as a filename.
- restore_session(FileNameOrMoreTDGargs,[]);
- false -> % The we want to use last session history!
- gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
- end.
-restore_session(FileName,MoreTDGargs) ->
- gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
-%% SessionNr=integer()
-%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
-%% NodeResult=ok | {error,Reason}
-%% NonDistributedNodeResult=[ok] | []
-%% Stops inviso tracing at all participating nodes. The inviso runtime components
-%% will go to state idle. It is now time to fetch the logfiles. Will most often
-%% succeed. Will only return an error if the entire inviso call returned an
-%% error. Not if an individual node failed stop tracing successfully.
-%% Any running trace case, including reactivator processes will be terminated.
-stop_session() ->
- gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reset_nodes()=NodeResult | {error,Reason}
-%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
-%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
-%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
-%% must not be having a running session.
-reset_nodes() ->
- gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
-reset_nodes(Nodes) ->
- gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% atc(TC,Id,Vars)=ok | {error,Reason}
-%% TC=atom(), name of the trace case.
-%% Id=term(), given name of this usage of TC.
-%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
-%% Function activating a trace case. The trace case must be defined in the
-%% trace case dictionary. The 'ok' return value is only a signal that the
-%% trace case has started successfully. It may then run for as long as it is
-%% programmed to run. An erroneous return value does not necessarily mean that
-%% the trace case has not been executed. It rather means that is undetermined
-%% what happend.
-atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
-%% sync_atc(TC,Id,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As atc/3 but waits for the trace case to finish.
-sync_atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_atc(TC,Id,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_rtc(TC,Vars)=Result | {error,Reason}
-%% sync_rtc(TC,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
-%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
-sync_rtc(TC,Vars) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_rtc(TC,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% dtc(TC,Id)=ok | {error,Reason}
-%% Deactivates a previosly activated trace case. This function can only be used
-%% on trace cases that has a deactivation defined in the trace case dictionary.
-%% There is of course really no difference between a file containing an activation
-%% compared to a deactivation. But to be able cancelling activations out from the
-%% history log, a defined deactivation is essential.
-%% As with activation, the returned 'ok' simply indicates the start of the trace
-%% case.
-dtc(TC,Id) ->
- gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_dtc(TC,Id)=Result | {error,Reason}
-%% sync_dtc(TC,Id,TimeOut)=
-%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
-%% parameters.
-sync_dtc(TC,Id) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_dtc(TC,Id,TimeOut) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% inviso(Cmd,Args)=Result
-%% Cmd=atom(), the (inviso) function name that shall be called.
-%% Args=list(), the arguments to Cmd.
-%% Result=term(), the result from the inviso function call.
-%% This function executes a Cmd in the inviso tool context. The inviso call will
-%% be logged in history log and thereby repeated in case of a reactivation.
-%% Note that this function is intended for use with inviso function API without
-%% specifying any nodes, since the function call is supposed to be carried out on
-%% all nodes.
-%% When these functions are written to an autostart config file by the tool there
-%% is supposed to be a translation to inviso_rt functions.
-inviso(Cmd,Args) ->
- gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reactivate()=ok | {error,Reason}
-%% reactivate(Node)=ok | {error,Reason}
-%% Moves a runtime component from suspended to the state running. This can be
-%% done for both tracing and inactive nodes. The later is necessary since you
-%% may have stopped tracing with a node suspended.
-%% In case the node is tracing, commands in the command history log are redone at
-%% the node in questions.
-%% Note that this function returns 'ok' before the node is running. This because the
-%% the reactivated history is done by a separate process and there is no guarantee
-%% when it will be ready. The reactivated node will not be marked as running in
-%% the tool until done reactivating.
-%% Further it is important to understand that if there are "ongoing" tracecases
-%% (i.e tracecase scripts that are currently executing) and this node was running
-%% at the time that tracecase script started to execute, the list of nodes bound
-%% to the Nodes variable in that script executer includes this node. Making it
-%% no longer suspended makes it start executing inviso commands from where ever
-%% such are called. Hence the reactivation may be interferred by that tracecase.
-reactivate() -> % Non-distributed API.
- reactivate(node()).
-reactivate(Node) ->
- gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
-%% Saves the currently collected command history log to a file. The file will
-%% be a binary-file. If FileName is an absolute path, it will be saved to that
-%% file. Otherwise the history dir will be used. If no history dir was specified
-%% the tool dir will be used, prepended to FileName.
-save_history(FileName) ->
- gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
-%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
-%% Dependency=inviso dependency parameter which will be used for every
-%% autostarted runtime component (included in Options).
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
-%% Options=add_nodes options to the inviso runtime component.
-%% M,F=atom(), the module and function for tracerdata generation.
-%% CompleteTDGargs=list(), all arguments as they are given to the tracer
-%% data generator function.
-%% AutostartData=[CaseSpec,...]
-%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
-%% FileName=string(), pointing out the trace case file. Note that this
-%% is the same as the path used by the tool.
-%% Bindings=Var bindings used according to the history for the
-%% invocation.
-%% M,F=atom(), the function that shall be called (normally some inviso).
-%% Args=list(), the actual arguments. Note that this may contain things
-%% which can not be written to file (ports, pids,...).
-%% Function returning information on how to autostart a node to make it trace
-%% according to the current history. The inviso_tool does not know how to write
-%% the necessary files at the nodes in question. That must be done by the user
-%% of the tool, guided by the return value from this function.
-%% Note that there will be two types of trace case files. Regular trace case
-%% files and binaries returned from this function. The latter contains the
-%% inviso commands which have been executed. Note that the order amongst the
-%% trace cases and binaries is of importance (otherwise they will be redone in
-%% an incorrect order).
-get_autostart_data(Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
-get_autostart_data(Nodes,Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_activities()={ok,Ongoing} | {error,Reason}
-%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
-%% TraceCases={tracecases,TraceCaseList}
-%% TraceCaseList=[{{TCname,Id},Phase},...]
-%% Phase=activating | deactivating
-%% Reactivators={reactivating_nodes,ReactivatingNodes}
-%% ReactivatingNodes=[Node,...]
-%% Returns a list of assynchronous tracecases and nodes doing reactivation at
-%% this momement. This can be useful to implement "home brewn" synchronization,
-%% waiting for the runtime components to reach a certain state.
-get_activities() ->
- gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_status(Node)={ok,StateStatus} | {error,Reason}
-%% StateStatus={State,Status} | reactivating | down
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-get_node_status() ->
- get_node_status(local_runtime).
-get_node_status(Node) ->
- gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
-%% Status=tracing | not_tracing, info about current/last session.
-%% SessionNr=integer()
-%% TDGargs=list(), list of the arguments that will be given to the tracer data
-%% generator function (not including the leading Nodes list).
-%% Returns data about the current or last session.
-get_session_data() ->
- gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
-%% flush(Nodes)={ok,NodesResults} | {error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Makes runtime components flush their trace ports.
-flush() ->
- gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
-flush(Nodes) ->
- gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_loopdata()=#ld
-%% Debug API returning the internal loopdata structure. See #ld above for details.
-get_loopdata() ->
- gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal APIs.
-%% -----------------------------------------------------------------------------
-
-%% tc_executer_reply(To,Reply)=nothing significant
-%% To=pid()
-%% Reply=term()
-%% Internal API used by a trace case executer process to signal its completion.
-tc_executer_reply(To,Reply) ->
- gen_server:cast(To,{tc_executer_reply,Reply}).
-%% -----------------------------------------------------------------------------
-
-%% Internal API used by a reactivator process indicating it is done with the
-%% history log it has got so far.
-%% Timeout set to infinity since the tool may be busy, then the reactivator just
-%% have to wait. If the tool crashes the reactivator will be go down too automatically.
-reactivator_reply(TPid,Counter) ->
- gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% gen_server implementation.
-%% =============================================================================
-
-init(Config) ->
- case fetch_configuration(Config) of % From conf-file and Config.
+ gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
+%% start_session(MoreTDGargs)=
+%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
+%% tracer data generator function.
+%% SessionNr=integer(), trace sessions are numbered by the tool.
+%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
+%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
+%% Initiates tracing at all participating nodes.
+start_session() ->
+ start_session([]).
+start_session(MoreTDGargs) ->
+ gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
+%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
+%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
+%% of the tool or not in state inactive will be marked as failing by the
+%% tool in the InvisoReturn.
+%% To reinitate a node means to (inviso) init tracing at it according to saved
+%% tracer data generator arguments for the current session and then redo the current
+%% history to bring it up to speed. Note that the tool must be running a session
+%% for reinitiate to work.
+reinitiate_session() ->
+ gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
+reinitiate_session(Nodes) ->
+ gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% restore_session()=
+%% restore_session(MoreTDGargs)=
+%% restore_session(FileName)=
+%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
+%% The two first clauses will start a new session using the last history. This
+%% implies that there must have been a session running prior.
+%% The two last clauses starts a session and reads a history file and executes the
+%% tracecases in it at all inactive nodes.
+%% In both cases the reused or read history becomes the current histoy, just if the
+%% session had been initiated manually. The tool may not
+%% have a session ongoing, and nodes already tracing (nodes which were adopted)
+%% are not effected. Just like when starting a session manually.
+restore_session() ->
+ restore_session([]).
+restore_session([]) -> % This cant be a filename.
+ gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
+restore_session(FileNameOrMoreTDGargs) ->
+ case is_string(FileNameOrMoreTDGargs) of
+ true -> % Interpret it as a filename.
+ restore_session(FileNameOrMoreTDGargs,[]);
+ false -> % The we want to use last session history!
+ gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
+ end.
+restore_session(FileName,MoreTDGargs) ->
+ gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
+%% SessionNr=integer()
+%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
+%% NodeResult=ok | {error,Reason}
+%% NonDistributedNodeResult=[ok] | []
+%% Stops inviso tracing at all participating nodes. The inviso runtime components
+%% will go to state idle. It is now time to fetch the logfiles. Will most often
+%% succeed. Will only return an error if the entire inviso call returned an
+%% error. Not if an individual node failed stop tracing successfully.
+%% Any running trace case, including reactivator processes will be terminated.
+stop_session() ->
+ gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reset_nodes()=NodeResult | {error,Reason}
+%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
+%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
+%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
+%% must not be having a running session.
+reset_nodes() ->
+ gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
+reset_nodes(Nodes) ->
+ gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% atc(TC,Id,Vars)=ok | {error,Reason}
+%% TC=atom(), name of the trace case.
+%% Id=term(), given name of this usage of TC.
+%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
+%% Function activating a trace case. The trace case must be defined in the
+%% trace case dictionary. The 'ok' return value is only a signal that the
+%% trace case has started successfully. It may then run for as long as it is
+%% programmed to run. An erroneous return value does not necessarily mean that
+%% the trace case has not been executed. It rather means that is undetermined
+%% what happend.
+atc(TC,Id,Vars) ->
+ gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
+%% sync_atc(TC,Id,Vars,TimeOut)=
+%% Result=term(), what ever is returned be the last expression in the trace case.
+%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
+%% As atc/3 but waits for the trace case to finish.
+sync_atc(TC,Id,Vars) ->
+ gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
+sync_atc(TC,Id,Vars,TimeOut) ->
+ gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% sync_rtc(TC,Vars)=Result | {error,Reason}
+%% sync_rtc(TC,Vars,TimeOut)=
+%% Result=term(), what ever is returned be the last expression in the trace case.
+%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
+%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
+%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
+sync_rtc(TC,Vars) ->
+ gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
+sync_rtc(TC,Vars,TimeOut) ->
+ gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% dtc(TC,Id)=ok | {error,Reason}
+%% Deactivates a previosly activated trace case. This function can only be used
+%% on trace cases that has a deactivation defined in the trace case dictionary.
+%% There is of course really no difference between a file containing an activation
+%% compared to a deactivation. But to be able cancelling activations out from the
+%% history log, a defined deactivation is essential.
+%% As with activation, the returned 'ok' simply indicates the start of the trace
+%% case.
+dtc(TC,Id) ->
+ gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% sync_dtc(TC,Id)=Result | {error,Reason}
+%% sync_dtc(TC,Id,TimeOut)=
+%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
+%% parameters.
+sync_dtc(TC,Id) ->
+ gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
+sync_dtc(TC,Id,TimeOut) ->
+ gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% inviso(Cmd,Args)=Result
+%% Cmd=atom(), the (inviso) function name that shall be called.
+%% Args=list(), the arguments to Cmd.
+%% Result=term(), the result from the inviso function call.
+%% This function executes a Cmd in the inviso tool context. The inviso call will
+%% be logged in history log and thereby repeated in case of a reactivation.
+%% Note that this function is intended for use with inviso function API without
+%% specifying any nodes, since the function call is supposed to be carried out on
+%% all nodes.
+%% When these functions are written to an autostart config file by the tool there
+%% is supposed to be a translation to inviso_rt functions.
+inviso(Cmd,Args) ->
+ gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reactivate()=ok | {error,Reason}
+%% reactivate(Node)=ok | {error,Reason}
+%% Moves a runtime component from suspended to the state running. This can be
+%% done for both tracing and inactive nodes. The later is necessary since you
+%% may have stopped tracing with a node suspended.
+%% In case the node is tracing, commands in the command history log are redone at
+%% the node in questions.
+%% Note that this function returns 'ok' before the node is running. This because the
+%% the reactivated history is done by a separate process and there is no guarantee
+%% when it will be ready. The reactivated node will not be marked as running in
+%% the tool until done reactivating.
+%% Further it is important to understand that if there are "ongoing" tracecases
+%% (i.e tracecase scripts that are currently executing) and this node was running
+%% at the time that tracecase script started to execute, the list of nodes bound
+%% to the Nodes variable in that script executer includes this node. Making it
+%% no longer suspended makes it start executing inviso commands from where ever
+%% such are called. Hence the reactivation may be interferred by that tracecase.
+reactivate() -> % Non-distributed API.
+ reactivate(node()).
+reactivate(Node) ->
+ gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
+%% Saves the currently collected command history log to a file. The file will
+%% be a binary-file. If FileName is an absolute path, it will be saved to that
+%% file. Otherwise the history dir will be used. If no history dir was specified
+%% the tool dir will be used, prepended to FileName.
+save_history(FileName) ->
+ gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
+%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
+%% Dependency=inviso dependency parameter which will be used for every
+%% autostarted runtime component (included in Options).
+%% NodeResults=[{Node,NodeResult},...]
+%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
+%% Options=add_nodes options to the inviso runtime component.
+%% M,F=atom(), the module and function for tracerdata generation.
+%% CompleteTDGargs=list(), all arguments as they are given to the tracer
+%% data generator function.
+%% AutostartData=[CaseSpec,...]
+%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
+%% FileName=string(), pointing out the trace case file. Note that this
+%% is the same as the path used by the tool.
+%% Bindings=Var bindings used according to the history for the
+%% invocation.
+%% M,F=atom(), the function that shall be called (normally some inviso).
+%% Args=list(), the actual arguments. Note that this may contain things
+%% which can not be written to file (ports, pids,...).
+%% Function returning information on how to autostart a node to make it trace
+%% according to the current history. The inviso_tool does not know how to write
+%% the necessary files at the nodes in question. That must be done by the user
+%% of the tool, guided by the return value from this function.
+%% Note that there will be two types of trace case files. Regular trace case
+%% files and binaries returned from this function. The latter contains the
+%% inviso commands which have been executed. Note that the order amongst the
+%% trace cases and binaries is of importance (otherwise they will be redone in
+%% an incorrect order).
+get_autostart_data(Dependency) ->
+ gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
+get_autostart_data(Nodes,Dependency) ->
+ gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_activities()={ok,Ongoing} | {error,Reason}
+%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
+%% TraceCases={tracecases,TraceCaseList}
+%% TraceCaseList=[{{TCname,Id},Phase},...]
+%% Phase=activating | deactivating
+%% Reactivators={reactivating_nodes,ReactivatingNodes}
+%% ReactivatingNodes=[Node,...]
+%% Returns a list of assynchronous tracecases and nodes doing reactivation at
+%% this momement. This can be useful to implement "home brewn" synchronization,
+%% waiting for the runtime components to reach a certain state.
+get_activities() ->
+ gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_status(Node)={ok,StateStatus} | {error,Reason}
+%% StateStatus={State,Status} | reactivating | down
+%% State=tracing | inactive | trace_failure
+%% Status=running | suspended
+get_node_status() ->
+ get_node_status(local_runtime).
+get_node_status(Node) ->
+ gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
+%% Status=tracing | not_tracing, info about current/last session.
+%% SessionNr=integer()
+%% TDGargs=list(), list of the arguments that will be given to the tracer data
+%% generator function (not including the leading Nodes list).
+%% Returns data about the current or last session.
+get_session_data() ->
+ gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
+%% flush(Nodes)={ok,NodesResults} | {error,Reason}
+%% NodeResults=[{Node,NodeResult},...]
+%% NodeResult=ok | {error,Reason}
+%% Makes runtime components flush their trace ports.
+flush() ->
+ gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
+flush(Nodes) ->
+ gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_loopdata()=#ld
+%% Debug API returning the internal loopdata structure. See #ld above for details.
+get_loopdata() ->
+ gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Internal APIs.
+%% -----------------------------------------------------------------------------
+
+%% tc_executer_reply(To,Reply)=nothing significant
+%% To=pid()
+%% Reply=term()
+%% Internal API used by a trace case executer process to signal its completion.
+tc_executer_reply(To,Reply) ->
+ gen_server:cast(To,{tc_executer_reply,Reply}).
+%% -----------------------------------------------------------------------------
+
+%% Internal API used by a reactivator process indicating it is done with the
+%% history log it has got so far.
+%% Timeout set to infinity since the tool may be busy, then the reactivator just
+%% have to wait. If the tool crashes the reactivator will be go down too automatically.
+reactivator_reply(TPid,Counter) ->
+ gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
+%% -----------------------------------------------------------------------------
+
+
+%% =============================================================================
+%% gen_server implementation.
+%% =============================================================================
+
+init(Config) ->
+ case fetch_configuration(Config) of % From conf-file and Config.
{ok,LD} when is_record(LD,ld) ->
- case start_inviso_at_c_node(LD) of
- {ok,CPid} ->
- LD2=start_runtime_components(LD),
- LD3=read_trace_case_definitions(LD2),
- process_flag(trap_exit,true),
- start_subscribe_inviso_events(LD3#ld.c_node),
- {ok,LD3#ld{c_pid=CPid}};
- {error,Reason} -> % Most likely already running.
- {stop,{error,Reason}}
- end;
- {error,Reason} ->
- {stop,{error,{start_up,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the inviso control component at node c_node, or "here"
-%% if it is not a distributed network.
-start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
- case inviso:start() of
- {ok,Pid} ->
- {ok,Pid};
- {error,Reason} ->
- {error,Reason}
- end;
-start_inviso_at_c_node(#ld{c_node=CNode}) ->
- case rpc:call(CNode,inviso,start,[]) of
- {ok,Pid} ->
- {ok,Pid};
- {error,{already_started,_}} -> % A control component already started.
- {error,{inviso_control_already_running,CNode}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the runtime components at all particapting nodes.
-%% It also updates the nodes structure in the #ld to indicate which nodes where
-%% successfully started. Returns a new #ld.
-%% Note that a runtime component may actually be running at one or several nodes.
-%% This is supposed to be the result of an (wanted) autostart. Meaning that the
-%% inviso tool can not handle the situation if a runtime component is not doing
-%% what it is supposed to do. In case a runtime component is already running it
-%% will be adopted and therefore marked as running.
-start_runtime_components(LD=#ld{c_node=undefined}) ->
- start_runtime_components_2(local_runtime,undefined,LD);
-start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
- start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
-start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
- start_runtime_components_2(Nodes,CNode,LD).
-
-start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(local_runtime,OptG),
- case inviso:add_node(mk_rt_tag(),Opts) of
- {ok,NAnsw} -> % Should be more clever really!
- NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
- LD#ld{nodes=NewNodesD};
- {error,_Reason} ->
- LD
- end;
-start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(Node,OptG),
- case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
- {ok,NodeResults} ->
- NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
- start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
- {error,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD);
- {badrpc,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD)
- end;
-start_runtime_components_2([],_,LD) ->
- LD.
-
-start_runtime_components_mk_opts(Node,{M,F,Args}) ->
- case catch apply(M,F,[Node|Args]) of
+ case start_inviso_at_c_node(LD) of
+ {ok,CPid} ->
+ LD2=start_runtime_components(LD),
+ LD3=read_trace_case_definitions(LD2),
+ process_flag(trap_exit,true),
+ start_subscribe_inviso_events(LD3#ld.c_node),
+ {ok,LD3#ld{c_pid=CPid}};
+ {error,Reason} -> % Most likely already running.
+ {stop,{error,Reason}}
+ end;
+ {error,Reason} ->
+ {stop,{error,{start_up,Reason}}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function starting the inviso control component at node c_node, or "here"
+%% if it is not a distributed network.
+start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
+ case inviso:start() of
+ {ok,Pid} ->
+ {ok,Pid};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+start_inviso_at_c_node(#ld{c_node=CNode}) ->
+ case rpc:call(CNode,inviso,start,[]) of
+ {ok,Pid} ->
+ {ok,Pid};
+ {error,{already_started,_}} -> % A control component already started.
+ {error,{inviso_control_already_running,CNode}};
+ {error,Reason} ->
+ {error,Reason};
+ {badrpc,Reason} ->
+ {error,{inviso_control_node_error,Reason}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function starting the runtime components at all particapting nodes.
+%% It also updates the nodes structure in the #ld to indicate which nodes where
+%% successfully started. Returns a new #ld.
+%% Note that a runtime component may actually be running at one or several nodes.
+%% This is supposed to be the result of an (wanted) autostart. Meaning that the
+%% inviso tool can not handle the situation if a runtime component is not doing
+%% what it is supposed to do. In case a runtime component is already running it
+%% will be adopted and therefore marked as running.
+start_runtime_components(LD=#ld{c_node=undefined}) ->
+ start_runtime_components_2(local_runtime,undefined,LD);
+start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
+ start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
+start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
+ start_runtime_components_2(Nodes,CNode,LD).
+
+start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
+ Opts=start_runtime_components_mk_opts(local_runtime,OptG),
+ case inviso:add_node(mk_rt_tag(),Opts) of
+ {ok,NAnsw} -> % Should be more clever really!
+ NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
+ LD#ld{nodes=NewNodesD};
+ {error,_Reason} ->
+ LD
+ end;
+start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
+ Opts=start_runtime_components_mk_opts(Node,OptG),
+ case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
+ {ok,NodeResults} ->
+ NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
+ start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
+ {error,_Reason} ->
+ start_runtime_components_2(Rest,CNode,LD);
+ {badrpc,_Reason} ->
+ start_runtime_components_2(Rest,CNode,LD)
+ end;
+start_runtime_components_2([],_,LD) ->
+ LD.
+
+start_runtime_components_mk_opts(Node,{M,F,Args}) ->
+ case catch apply(M,F,[Node|Args]) of
{ok,Opts} when is_list(Opts) ->
- start_runtime_component_mk_opts_add_dependency(Opts);
- _ ->
- [?DEFAULT_DEPENDENCY]
- end.
-
-%% The options generator is not supposed to generate the dependency. Hence this
-%% function adds and if necessary removes an incorrectly added dependency tag.
-start_runtime_component_mk_opts_add_dependency(Opts) ->
- case lists:keysearch(dependency,1,Opts) of
- {value,_} -> % Not allowed!!!
- [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
- false ->
- [?DEFAULT_DEPENDENCY|Opts]
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function subscribing to inviso events from the inviso controller. This
-%% will make it possible to follow runtime components going down.
-start_subscribe_inviso_events(undefined) ->
- inviso:subscribe();
-start_subscribe_inviso_events(CNode) ->
- rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% gen_server handle call back functions.
-%% -----------------------------------------------------------------------------
-
-handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
- when is_list(UntouchedNodes) ->
- {stop,
- normal,
- remove_all_trace_patterns(CNode,
- UntouchedNodes++KeepNodes,
- get_available_nodes(NodesD)),
- LD};
-handle_call({stop,BadArg},_From,LD) ->
- {reply,{error,{badarg,BadArg}},LD};
-
-handle_call({reconnect_nodes,Nodes},_From,LD) ->
- case h_reconnect_nodes(Nodes,LD) of
- {ok,{Nodes2,NodesErr,NewLD}} ->
- if
- Nodes==local_runtime ->
- {reply,
- build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
- NewLD};
+ start_runtime_component_mk_opts_add_dependency(Opts);
+ _ ->
+ [?DEFAULT_DEPENDENCY]
+ end.
+
+%% The options generator is not supposed to generate the dependency. Hence this
+%% function adds and if necessary removes an incorrectly added dependency tag.
+start_runtime_component_mk_opts_add_dependency(Opts) ->
+ case lists:keysearch(dependency,1,Opts) of
+ {value,_} -> % Not allowed!!!
+ [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
+ false ->
+ [?DEFAULT_DEPENDENCY|Opts]
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function subscribing to inviso events from the inviso controller. This
+%% will make it possible to follow runtime components going down.
+start_subscribe_inviso_events(undefined) ->
+ inviso:subscribe();
+start_subscribe_inviso_events(CNode) ->
+ rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% gen_server handle call back functions.
+%% -----------------------------------------------------------------------------
+
+handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
+ when is_list(UntouchedNodes) ->
+ {stop,
+ normal,
+ remove_all_trace_patterns(CNode,
+ UntouchedNodes++KeepNodes,
+ get_available_nodes(NodesD)),
+ LD};
+handle_call({stop,BadArg},_From,LD) ->
+ {reply,{error,{badarg,BadArg}},LD};
+
+handle_call({reconnect_nodes,Nodes},_From,LD) ->
+ case h_reconnect_nodes(Nodes,LD) of
+ {ok,{Nodes2,NodesErr,NewLD}} ->
+ if
+ Nodes==local_runtime ->
+ {reply,
+ build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
+ NewLD};
is_list(Nodes) ->
- {reply,
- {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
- NewLD}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
-
-handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % No session running.
- if
+ {reply,
+ {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
+ NewLD}
+ end;
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+
+handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ false -> % No session running.
+ if
is_list(MoreTDGargs) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
- NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
- Nodes2,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true -> % Faulty TDGargs.
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true -> % The tool must be tracing.
- {M,F,_Args}=LD#ld.tdg,
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
- {ok,{NodesErr,ReturnVal,NewLD}} ->
- {reply,
- {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
- NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Must have a running session!
- {reply,{error,no_session},LD}
- end;
-
-handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
+ DateTime=calendar:universal_time(),
+ {M,F,Args}=LD#ld.tdg,
+ TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
+ case h_start_session(M,F,TDGargs,LD) of
+ {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
+ NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
+ NewLD#ld{chl=mk_chl(LD#ld.chl)}),
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD2#ld{session_state=tracing_sessionstate()}};
+ {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
+ NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
+ Nodes2,
+ NewLD#ld{chl=mk_chl(LD#ld.chl)}),
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD2#ld{session_state=tracing_sessionstate()}};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true -> % Faulty TDGargs.
+ {reply,{error,{badarg,MoreTDGargs}},LD}
+ end;
+ true ->
+ {reply,{error,session_already_started},LD}
+ end;
+
+handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true -> % The tool must be tracing.
+ {M,F,_Args}=LD#ld.tdg,
+ TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
+ case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
+ {ok,{NodesErr,ReturnVal,NewLD}} ->
+ {reply,
+ {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
+ NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ false -> % Must have a running session!
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
when is_list(MoreTDGargs) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case catch make_absolute_path(FileName,LD#ld.dir) of
+ case is_tracing(LD#ld.session_state) of
+ false ->
+ case catch make_absolute_path(FileName,LD#ld.dir) of
AbsFileName when is_list(AbsFileName) ->
- case file:read_file(AbsFileName) of
- {ok,Bin} ->
- if
+ case file:read_file(AbsFileName) of
+ {ok,Bin} ->
+ if
is_list(MoreTDGargs) ->
- case catch replace_history_chl(OldCHL,
- binary_to_term(Bin)) of
- {ok,CHL} -> % The file was well formatted.
- case h_restore_session(MoreTDGargs,
- LD#ld{chl=CHL}) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=
- tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- Error -> % Badly formatted file.
- {reply,
- {error,{bad_file,{AbsFileName,Error}}},
- LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- {error,Reason} ->
- {reply,{error,{read_file,Reason}},LD}
- end;
- Error ->
- {reply,{error,{bad_filename,{FileName,Error}}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-%% This is doing restore session on the current history.
-handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case history_exists_chl(CHL) of
- true -> % There is a history to redo.
- if
+ case catch replace_history_chl(OldCHL,
+ binary_to_term(Bin)) of
+ {ok,CHL} -> % The file was well formatted.
+ case h_restore_session(MoreTDGargs,
+ LD#ld{chl=CHL}) of
+ {ok,{SessionNr,ReturnVal,NewLD}} ->
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD#ld{session_state=
+ tracing_sessionstate()}};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ Error -> % Badly formatted file.
+ {reply,
+ {error,{bad_file,{AbsFileName,Error}}},
+ LD}
+ end;
+ true ->
+ {reply,{error,{badarg,MoreTDGargs}},LD}
+ end;
+ {error,Reason} ->
+ {reply,{error,{read_file,Reason}},LD}
+ end;
+ Error ->
+ {reply,{error,{bad_filename,{FileName,Error}}},LD}
+ end;
+ true ->
+ {reply,{error,session_already_started},LD}
+ end;
+%% This is doing restore session on the current history.
+handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
+ case is_tracing(LD#ld.session_state) of
+ false ->
+ case history_exists_chl(CHL) of
+ true -> % There is a history to redo.
+ if
is_list(MoreTDGargs) ->
- case h_restore_session(MoreTDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- false ->
- {reply,{error,no_history},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-%% To stop tracing means stop_tracing through the inviso API. But we must also
-%% remove any help processes executing inviso commands (trace case executers
-%% and reactivators).
-%% Note that to be really sure we should actually wait for EXIT-signals from those
-%% processes before returning a successful returnvalue to the caller. In theory
-%% those processes could issue an inviso call effecting a new trace session started
-%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
-%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
-%% if the node is tracing or not
-handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
- case is_tracing(SState) of
- true ->
- NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
- NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
- case h_stop_session(LD) of
- {ok,{SessionNr,Result}} ->
- NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
- {reply,
- {ok,{SessionNr,Result}},
- LD#ld{session_state=passive_sessionstate(),
- nodes=NewNodesD,
- chl=NewCHL,
- reactivators=NewReAct,
- started_initial_tcs=[]}};
- {error,Reason} -> % Now we're really in deep shit :-)
- {reply,{error,{unrecoverable,Reason}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % We can not be in a session.
- {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
- true ->
- {reply,{error,session_active},LD}
- end;
-
-%% Calling a trace-case, or "turning it on".
-handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_atc(TC,Id,Vars,LD) of
- {ok,NewLD} -> % Trace case executed.
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
+ case h_restore_session(MoreTDGargs,LD) of
+ {ok,{SessionNr,ReturnVal,NewLD}} ->
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD#ld{session_state=tracing_sessionstate()}};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,MoreTDGargs}},LD}
+ end;
+ false ->
+ {reply,{error,no_history},LD}
+ end;
+ true ->
+ {reply,{error,session_already_started},LD}
+ end;
+
+%% To stop tracing means stop_tracing through the inviso API. But we must also
+%% remove any help processes executing inviso commands (trace case executers
+%% and reactivators).
+%% Note that to be really sure we should actually wait for EXIT-signals from those
+%% processes before returning a successful returnvalue to the caller. In theory
+%% those processes could issue an inviso call effecting a new trace session started
+%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
+%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
+%% if the node is tracing or not
+handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
+ case is_tracing(SState) of
+ true ->
+ NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
+ NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
+ case h_stop_session(LD) of
+ {ok,{SessionNr,Result}} ->
+ NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
+ {reply,
+ {ok,{SessionNr,Result}},
+ LD#ld{session_state=passive_sessionstate(),
+ nodes=NewNodesD,
+ chl=NewCHL,
+ reactivators=NewReAct,
+ started_initial_tcs=[]}};
+ {error,Reason} -> % Now we're really in deep shit :-)
+ {reply,{error,{unrecoverable,Reason}},LD}
+ end;
+ false ->
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ false -> % We can not be in a session.
+ {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
+ true ->
+ {reply,{error,session_active},LD}
+ end;
+
+%% Calling a trace-case, or "turning it on".
+handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of % Check that we are tracing now.
+ true ->
+ case h_atc(TC,Id,Vars,LD) of
+ {ok,NewLD} -> % Trace case executed.
+ {reply,ok,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ false -> % Can't activate if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true ->
+ if
is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
+ case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
+ {ok,NewLD,Result} ->
+ {reply,Result,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,TimeOut}},LD}
+ end;
+ false ->
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true ->
+ if
is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_rtc(TC,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-
-handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_dtc(TC,Id,LD) of
- {ok,NewLD} ->
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- if
+ case h_sync_rtc(TC,Vars,TimeOut,LD) of
+ {ok,NewLD,Result} ->
+ {reply,Result,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,TimeOut}},LD}
+ end;
+ false ->
+ {reply,{error,no_session},LD}
+ end;
+
+
+handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of % Check that we are tracing now.
+ true ->
+ case h_dtc(TC,Id,LD) of
+ {ok,NewLD} ->
+ {reply,ok,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ false -> % Can't activate if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of % Check that we are tracing now.
+ true ->
+ if
is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_dtc(TC,Id,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
+ case h_sync_dtc(TC,Id,TimeOut,LD) of
+ {ok,NewLD,Result} ->
+ {reply,Result,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,TimeOut}},LD}
+ end;
+ false -> % Can't activate if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true ->
+ if
is_list(Args) ->
- case h_inviso(Cmd,Args,LD) of
- {ok,{Reply,NewLD}} ->
- {reply,Reply,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,Args}},LD}
- end;
- false -> % Can't do if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
- case get_state_nodes(Node,NodesD) of
- {trace_failure,_} ->
- {reply,{error,trace_failure},LD};
- {State,suspended} -> % The node is infact suspended.
- case h_reactivate(Node,CNode) of
- ok ->
- case {State,is_tracing(LD#ld.session_state)} of
- {tracing,true} -> % Only then shall we redo cmds.
- {reply,ok,redo_cmd_history(Node,LD)};
- _ -> % All other just no longer suspended.
- {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- reactivating ->
- {reply,{error,reactivating},LD};
- {_,running} ->
- {reply,{error,already_running},LD};
- down ->
- {reply,{error,not_available},LD};
- false ->
- {reply,{error,unknown_node},LD}
- end;
-
-handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
- case lists:keysort(2,get_loglist_chl(CHL)) of
- [] -> % Empty history or no history.
- {reply,{error,no_history},LD};
- Log ->
- case h_save_history(HDir,Dir,FileName,Log) of
- {ok,AbsFileName} ->
- {reply,{ok,AbsFileName},LD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end
- end;
-
-
-handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
- case build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict) of
- {ok,ASD} ->
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- {M,F,_}=LD#ld.tdg,
- OptsG=LD#ld.optg, % Addnodes options generator.
- {reply,
- h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
- LD};
- {error,Reason} -> % Bad datatypes in command args.
- {reply,{error,Reason},LD}
- end;
-
-handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
- handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
-handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
- Nodes=get_all_nodenames_nodes(NodesD),
- handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
-
-handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
- TraceCases=get_ongoing_chl(CHL),
- RNodes=get_all_nodes_reactivators(Reactivators),
- ReturnList1=
- if
- TraceCases==[] ->
- [];
- true ->
- [{tracecases,TraceCases}]
- end,
- ReturnList2=
- if
- RNodes==[] ->
- ReturnList1;
- true ->
- [{reactivating_nodes,RNodes}|ReturnList1]
- end,
- {reply,{ok,ReturnList2},LD};
-
-handle_call({get_node_status,Node},_Node,LD) ->
- case get_state_nodes(Node,LD#ld.nodes) of
- false ->
- {reply,{error,unknown_node},LD};
- StateStatus ->
- {reply,{ok,StateStatus},LD}
- end;
-
-handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
- case get_latest_session_nr_tracer_data(TD) of
- undefined ->
- {reply,{error,no_session},LD};
- SessionNr ->
- TDGargs=get_latest_tdgargs_tracer_data(TD),
- case is_tracing(SState) of
- true ->
- {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
- false ->
- {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
- end
- end;
-
-handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- Nodes=get_tracing_nodes(NodesD),
- {reply,h_flush(CNode,Nodes),LD};
-handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
- {reply,h_flush(CNode,Nodes),LD};
-
-handle_call(get_loopdata,_From,LD) ->
- {reply,LD,LD};
-
-%% Internal handle_call callbacks.
-
-handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
- HighestUsedCounter=get_highest_used_counter_chl(CHL),
- if
- HighestUsedCounter>Counter -> % There are now more log entries.
- NewUnsortedLog=get_loglist_chl(CHL),
- {reply,{more,NewUnsortedLog},LD};
- true -> % No Counter is youngest log entry.
- NodesD=LD#ld.nodes,
- Node=get_node_reactivators(RPid,LD#ld.reactivators),
- {reply,
- done,
- LD#ld{nodes=set_running_nodes(Node,NodesD),
- reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Handling a notification from a trace case execution process. Receiving this
-%% indicated that this phase of the trace case is finnished.
-handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
- case Phase of
- activating -> % The trace case is running now.
- {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
- {noreply,NewLD};
- stopping ->
- {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
- {noreply,NewLD};
- _ ->
- {noreply,LD}
- end;
-handle_cast(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% This is the case when a runtime component goes down. We stop all running
-%% reactivators for this node. Note that there can also be tracecases ongoing
-%% where this node is part of the Nodes variable. But there is not much we can
-%% do about that. Other then informing the user that it is unwise to reconnect
-%% this node before those tracecases have stopped being ongoing.
-handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
- {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-%% This is the case when a runtime component gets suspended. Much of the same
-%% problem as described above applies.
-handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
- {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-handle_info(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% Called when the tool server stops. First clause, termination is initiated by
-%% our self and therefore controlled another way. In the second case we are
-%% stopping for some external reason, and we must then do more here in terminate/2.
-terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
- stop_inviso_at_c_node(CNode);
-terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
- remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
- stop_inviso_at_c_node(CNode).
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% =============================================================================
-%% Handler first level help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% reconnect_nodes
-%% -----------------------------------------------------------------------------
-
-%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
-%% the set of nodes handled by the tool. It is not possible to reconnect a node
-%% that is not marked as down. This partly because we otherwise risk losing the
-%% trace_failure state (which can not be rediscovered).
-h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
- _ -> % Allready connected!
- {ok,{[],{error,already_connected},LD}}
- end;
+ case h_inviso(Cmd,Args,LD) of
+ {ok,{Reply,NewLD}} ->
+ {reply,Reply,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,Args}},LD}
+ end;
+ false -> % Can't do if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
+ case get_state_nodes(Node,NodesD) of
+ {trace_failure,_} ->
+ {reply,{error,trace_failure},LD};
+ {State,suspended} -> % The node is infact suspended.
+ case h_reactivate(Node,CNode) of
+ ok ->
+ case {State,is_tracing(LD#ld.session_state)} of
+ {tracing,true} -> % Only then shall we redo cmds.
+ {reply,ok,redo_cmd_history(Node,LD)};
+ _ -> % All other just no longer suspended.
+ {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
+ end;
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ reactivating ->
+ {reply,{error,reactivating},LD};
+ {_,running} ->
+ {reply,{error,already_running},LD};
+ down ->
+ {reply,{error,not_available},LD};
+ false ->
+ {reply,{error,unknown_node},LD}
+ end;
+
+handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
+ case lists:keysort(2,get_loglist_chl(CHL)) of
+ [] -> % Empty history or no history.
+ {reply,{error,no_history},LD};
+ Log ->
+ case h_save_history(HDir,Dir,FileName,Log) of
+ {ok,AbsFileName} ->
+ {reply,{ok,AbsFileName},LD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end
+ end;
+
+handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
+ {ok,ASD} = build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict),
+ TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
+ {M,F,_}=LD#ld.tdg,
+ OptsG=LD#ld.optg, % Addnodes options generator.
+ {reply,
+ h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
+ LD};
+
+handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
+ handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
+handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
+ Nodes=get_all_nodenames_nodes(NodesD),
+ handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
+
+handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
+ TraceCases=get_ongoing_chl(CHL),
+ RNodes=get_all_nodes_reactivators(Reactivators),
+ ReturnList1=
+ if
+ TraceCases==[] ->
+ [];
+ true ->
+ [{tracecases,TraceCases}]
+ end,
+ ReturnList2=
+ if
+ RNodes==[] ->
+ ReturnList1;
+ true ->
+ [{reactivating_nodes,RNodes}|ReturnList1]
+ end,
+ {reply,{ok,ReturnList2},LD};
+
+handle_call({get_node_status,Node},_Node,LD) ->
+ case get_state_nodes(Node,LD#ld.nodes) of
+ false ->
+ {reply,{error,unknown_node},LD};
+ StateStatus ->
+ {reply,{ok,StateStatus},LD}
+ end;
+
+handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
+ case get_latest_session_nr_tracer_data(TD) of
+ undefined ->
+ {reply,{error,no_session},LD};
+ SessionNr ->
+ TDGargs=get_latest_tdgargs_tracer_data(TD),
+ case is_tracing(SState) of
+ true ->
+ {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
+ false ->
+ {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
+ end
+ end;
+
+handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
+ Nodes=get_tracing_nodes(NodesD),
+ {reply,h_flush(CNode,Nodes),LD};
+handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
+ {reply,h_flush(CNode,Nodes),LD};
+
+handle_call(get_loopdata,_From,LD) ->
+ {reply,LD,LD};
+
+%% Internal handle_call callbacks.
+
+handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
+ HighestUsedCounter=get_highest_used_counter_chl(CHL),
+ if
+ HighestUsedCounter>Counter -> % There are now more log entries.
+ NewUnsortedLog=get_loglist_chl(CHL),
+ {reply,{more,NewUnsortedLog},LD};
+ true -> % No Counter is youngest log entry.
+ NodesD=LD#ld.nodes,
+ Node=get_node_reactivators(RPid,LD#ld.reactivators),
+ {reply,
+ done,
+ LD#ld{nodes=set_running_nodes(Node,NodesD),
+ reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Handling a notification from a trace case execution process. Receiving this
+%% indicated that this phase of the trace case is finnished.
+handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
+ case Phase of
+ activating -> % The trace case is running now.
+ {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
+ {noreply,NewLD};
+ stopping ->
+ {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
+ {noreply,NewLD};
+ _ ->
+ {noreply,LD}
+ end;
+handle_cast(_,LD) ->
+ {noreply,LD}.
+%% -----------------------------------------------------------------------------
+
+%% This is the case when a runtime component goes down. We stop all running
+%% reactivators for this node. Note that there can also be tracecases ongoing
+%% where this node is part of the Nodes variable. But there is not much we can
+%% do about that. Other then informing the user that it is unwise to reconnect
+%% this node before those tracecases have stopped being ongoing.
+handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
+ {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
+ reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
+
+%% This is the case when a runtime component gets suspended. Much of the same
+%% problem as described above applies.
+handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
+ {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
+ reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
+
+handle_info(_,LD) ->
+ {noreply,LD}.
+%% -----------------------------------------------------------------------------
+
+%% Called when the tool server stops. First clause, termination is initiated by
+%% our self and therefore controlled another way. In the second case we are
+%% stopping for some external reason, and we must then do more here in terminate/2.
+terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
+ stop_inviso_at_c_node(CNode);
+terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
+ remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
+ stop_inviso_at_c_node(CNode).
+%% -----------------------------------------------------------------------------
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% =============================================================================
+%% Handler first level help functions.
+%% =============================================================================
+
+%% -----------------------------------------------------------------------------
+%% reconnect_nodes
+%% -----------------------------------------------------------------------------
+
+%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
+%% the set of nodes handled by the tool. It is not possible to reconnect a node
+%% that is not marked as down. This partly because we otherwise risk losing the
+%% trace_failure state (which can not be rediscovered).
+h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
+ case get_state_nodes(local_runtime,NodesD) of
+ down ->
+ {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
+ _ -> % Allready connected!
+ {ok,{[],{error,already_connected},LD}}
+ end;
h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when is_list(Nodes) ->
- {Nodes2,NodesErr}=
- lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- down -> % Yes this node can be reconnected.
- {[N|Nodes2],NodesErr};
- false -> % Not part of the node-set!
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ -> % Allready connected!
- {Nodes2,[{N,{error,already_connected}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes),
- LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
- {ok,{Nodes2,NodesErr,LD2}};
-h_reconnect_nodes(Nodes,_LD) ->
- {error,{badarg,Nodes}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% start_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the tracing at all nodes. Note that the tracer data
-%% is calculated using a user defined function. This is how for instance the
-%% file names (of the log files) are determined.
-%% Before the nodes are initiated their (possibly remaining) trace patterns are
-%% cleared, both local and global.
-h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case get_inactive_running_nodes(NodesD) of
- [] -> % There are no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,[]);
- Nodes -> % List of nodes or 'local_runtime'.
- case h_start_session_ctp_all(CNode,Nodes) of
- {ok,Errors,[]} -> % Now no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,Errors);
- {ok,Errors,Nodes2} -> % Now these nodes are fresh.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} -> % Generated our tracerdata.
- case h_start_session_2(CNode,TracerList,Errors) of
- {ok,ReturnValue} -> % Some nodes are initialized now.
- {NewNodesD,Nodes3}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- {ok,{SessionNr,
- ReturnValue,
- Nodes3, % The nodes that shall get initial tracases.
- LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} -> % Faulty tracer data generator func.
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} -> % Error clearing patterns.
- {error,Reason}
- end
- end.
-
-h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- if
- CNode==undefined ->
- {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
- true ->
- {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
- end.
-
-%% Help function clearing all trace patterns on all nodes.
-h_start_session_ctp_all(CNode,Nodes) ->
- case remove_all_trace_patterns(CNode,[],Nodes) of
- ok -> % Non-distributed case1.
- {ok,[],local_runtime};
- {error,Reason} -> % Non-distributed case2 and general failure.
- {error,Reason};
- {ok,NodeResults} ->
- h_start_session_ctp_all_2(NodeResults,[],[])
- end.
-
-h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
-h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
-h_start_session_ctp_all_2([],Errors,Nodes) ->
- {ok,Errors,Nodes}.
-
-%% Help function doing the actual init_tracing.
-h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
- case inviso:init_tracing(TracerData) of
+ {Nodes2,NodesErr}=
+ lists:foldl(fun(N,{Nodes2,NodesErr})->
+ case get_state_nodes(N,NodesD) of
+ down -> % Yes this node can be reconnected.
+ {[N|Nodes2],NodesErr};
+ false -> % Not part of the node-set!
+ {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
+ _ -> % Allready connected!
+ {Nodes2,[{N,{error,already_connected}}|NodesErr]}
+ end
+ end,
+ {[],[]},
+ Nodes),
+ LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
+ {ok,{Nodes2,NodesErr,LD2}};
+h_reconnect_nodes(Nodes,_LD) ->
+ {error,{badarg,Nodes}}.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% start_session
+%% -----------------------------------------------------------------------------
+
+%% Help function starting the tracing at all nodes. Note that the tracer data
+%% is calculated using a user defined function. This is how for instance the
+%% file names (of the log files) are determined.
+%% Before the nodes are initiated their (possibly remaining) trace patterns are
+%% cleared, both local and global.
+h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
+ case get_inactive_running_nodes(NodesD) of
+ [] -> % There are no nodes to initiate!
+ h_start_session_nonodes(TDGargs,LD,[]);
+ Nodes -> % List of nodes or 'local_runtime'.
+ case h_start_session_ctp_all(CNode,Nodes) of
+ {ok,Errors,[]} -> % Now no nodes to initiate!
+ h_start_session_nonodes(TDGargs,LD,Errors);
+ {ok,Errors,Nodes2} -> % Now these nodes are fresh.
+ case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
+ {ok,TracerList} -> % Generated our tracerdata.
+ case h_start_session_2(CNode,TracerList,Errors) of
+ {ok,ReturnValue} -> % Some nodes are initialized now.
+ {NewNodesD,Nodes3}=
+ set_tracing_running_nodes(CNode,ReturnValue,NodesD),
+ {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
+ {ok,{SessionNr,
+ ReturnValue,
+ Nodes3, % The nodes that shall get initial tracases.
+ LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ {error,Reason} -> % Faulty tracer data generator func.
+ {error,{bad_tdg,Reason}}
+ end;
+ {error,Reason} -> % Error clearing patterns.
+ {error,Reason}
+ end
+ end.
+
+h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
+ {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
+ if
+ CNode==undefined ->
+ {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
+ true ->
+ {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
+ end.
+
+%% Help function clearing all trace patterns on all nodes.
+h_start_session_ctp_all(CNode,Nodes) ->
+ case remove_all_trace_patterns(CNode,[],Nodes) of
+ ok -> % Non-distributed case1.
+ {ok,[],local_runtime};
+ {error,Reason} -> % Non-distributed case2 and general failure.
+ {error,Reason};
+ {ok,NodeResults} ->
+ h_start_session_ctp_all_2(NodeResults,[],[])
+ end.
+
+h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
+ h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
+h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
+ h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
+h_start_session_ctp_all_2([],Errors,Nodes) ->
+ {ok,Errors,Nodes}.
+
+%% Help function doing the actual init_tracing.
+h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
+ case inviso:init_tracing(TracerData) of
{ok,LogResult} when is_list(LogResult) ->
- {ok,{ok,LogResult}};
- {error,already_initated} -> % Perhaps adopted!?
- {ok,{error,already_initiated}}; % Not necessarily wrong.
- {error,Reason} ->
- {error,Reason}
- end;
-h_start_session_2(CNode,TracerList,Errors) ->
- case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
- {ok,NodeResults} ->
- {ok,{ok,Errors++NodeResults}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting all initial trace cases. They are actually handled
-%% the same way as user started trace cases. We actually only start initial
-%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
-%% adopted some nodes some already tracing nodes, and such are supposed to have
-%% the correct patterns and flags set.
-do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
- Id=make_ref(), % Trace case ID.
- case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
- {ok,NewLD} -> % Trace case was successfully started.
- NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
- do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
- {error,_Reason} ->
- do_initial_tcs(Rest,Nodes,LD)
- end;
-do_initial_tcs([_|Rest],Nodes,LD) ->
- do_initial_tcs(Rest,Nodes,LD);
-do_initial_tcs([],_Nodes,LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% This help functio is used instead of do_initial_tcs/3 if there actually are no
-%% nodes to do the trace cases on. The reason we must have this function is that
-%% the tracecases must still be entered into the history with bindings and all.
-%% But we let them be marked as 'running' immediately (no need for the activator
-%% process).
-add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} ->
- Id=make_ref(), % Trace case ID.
- FakeProcH=make_ref(), % Need something to enter as activator.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
- NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
- add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
- started_initial_tcs=NewInitialTcs});
- {error,_Reason} -> % Not much we can do about that.
- add_initial_tcs_to_history(Rest,LD)
- end;
- false ->
- add_initial_tcs_to_history(Rest,LD)
- end;
-add_initial_tcs_to_history([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reinitiate_session
-%% -----------------------------------------------------------------------------
-
-%% Function doing the reinitiation. That means first do init_tracing at the nodes
-%% in question. Then redo the command history to bring them up to speed.
-%% But first the runtime component is cleared of all trace patterns.
-h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- case h_reinitiate_session_2(Nodes,NodesD,CNode) of
- {ok,{[],NodesErr}} -> % No nodes to reinitiate.
- {ok,{NodesErr,{ok,[]},LD}};
- {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} ->
- case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
- {ok,ReturnValue} -> % Ok, now we must redo cmd history.
- {NewNodesD,_Nodes}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
- {ok,{NodesErr,ReturnValue,NewLD}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Help function finding out which nodes in Nodes actually can be reinitiated.
-%% A node must be up, inactive and not suspended in order for this to work. All the
-%% rest is just a matter of how detailed error return values we want to generate.
-h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
- case get_state_nodes(local_runtime,NodesD) of
- {inactive,running} -> % Only ok case.
- case inviso:ctp_all() of
- ok ->
- {ok,{local_runtime,[]}};
- {error,Reason} -> % This is strange.
- {error,Reason}
- end;
- {_,suspended} ->
- {ok,{[],{error,suspended}}};
- down ->
- {ok,{[],{error,down}}};
- _ ->
- {ok,{[],{error,already_in_session}}}
- end;
+ {ok,{ok,LogResult}};
+ {error,already_initated} -> % Perhaps adopted!?
+ {ok,{error,already_initiated}}; % Not necessarily wrong.
+ {error,Reason} ->
+ {error,Reason}
+ end;
+h_start_session_2(CNode,TracerList,Errors) ->
+ case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
+ {ok,NodeResults} ->
+ {ok,{ok,Errors++NodeResults}};
+ {error,Reason} ->
+ {error,Reason};
+ {badrpc,Reason} ->
+ {error,{inviso_control_node_error,Reason}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function starting all initial trace cases. They are actually handled
+%% the same way as user started trace cases. We actually only start initial
+%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
+%% adopted some nodes some already tracing nodes, and such are supposed to have
+%% the correct patterns and flags set.
+do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
+ Id=make_ref(), % Trace case ID.
+ case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
+ {ok,NewLD} -> % Trace case was successfully started.
+ NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
+ do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
+ {error,_Reason} ->
+ do_initial_tcs(Rest,Nodes,LD)
+ end;
+do_initial_tcs([_|Rest],Nodes,LD) ->
+ do_initial_tcs(Rest,Nodes,LD);
+do_initial_tcs([],_Nodes,LD) ->
+ LD.
+%% -----------------------------------------------------------------------------
+
+%% This help functio is used instead of do_initial_tcs/3 if there actually are no
+%% nodes to do the trace cases on. The reason we must have this function is that
+%% the tracecases must still be entered into the history with bindings and all.
+%% But we let them be marked as 'running' immediately (no need for the activator
+%% process).
+add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} ->
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} ->
+ Id=make_ref(), % Trace case ID.
+ FakeProcH=make_ref(), % Need something to enter as activator.
+ NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
+ NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
+ NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
+ add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
+ started_initial_tcs=NewInitialTcs});
+ {error,_Reason} -> % Not much we can do about that.
+ add_initial_tcs_to_history(Rest,LD)
+ end;
+ false ->
+ add_initial_tcs_to_history(Rest,LD)
+ end;
+add_initial_tcs_to_history([],LD) ->
+ LD.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% reinitiate_session
+%% -----------------------------------------------------------------------------
+
+%% Function doing the reinitiation. That means first do init_tracing at the nodes
+%% in question. Then redo the command history to bring them up to speed.
+%% But first the runtime component is cleared of all trace patterns.
+h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
+ case h_reinitiate_session_2(Nodes,NodesD,CNode) of
+ {ok,{[],NodesErr}} -> % No nodes to reinitiate.
+ {ok,{NodesErr,{ok,[]},LD}};
+ {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
+ case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
+ {ok,TracerList} ->
+ case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
+ {ok,ReturnValue} -> % Ok, now we must redo cmd history.
+ {NewNodesD,_Nodes}=
+ set_tracing_running_nodes(CNode,ReturnValue,NodesD),
+ NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
+ {ok,{NodesErr,ReturnValue,NewLD}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ {error,Reason} ->
+ {error,{bad_tdg,Reason}}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+%% Help function finding out which nodes in Nodes actually can be reinitiated.
+%% A node must be up, inactive and not suspended in order for this to work. All the
+%% rest is just a matter of how detailed error return values we want to generate.
+h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
+ case get_state_nodes(local_runtime,NodesD) of
+ {inactive,running} -> % Only ok case.
+ case inviso:ctp_all() of
+ ok ->
+ {ok,{local_runtime,[]}};
+ {error,Reason} -> % This is strange.
+ {error,Reason}
+ end;
+ {_,suspended} ->
+ {ok,{[],{error,suspended}}};
+ down ->
+ {ok,{[],{error,down}}};
+ _ ->
+ {ok,{[],{error,already_in_session}}}
+ end;
h_reinitiate_session_2(Nodes,NodesD,CNode) when is_list(Nodes) ->
- {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- {inactive,running} -> % Only ok case.
- case rpc:call(CNode,inviso,ctp_all,[[N]]) of
- {ok,[{N,ok}]} ->
- {[N|Nodes2],NodesErr};
- {ok,[{N,{error,Reason}}]} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {error,Reason} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {badrpc,Reason} ->
- {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
- end;
- {_,suspended} ->
- {Nodes2,[{N,{error,suspended}}|NodesErr]};
- down ->
- {Nodes2,[{N,{error,down}}|NodesErr]};
- false ->
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ ->
- {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes)};
-h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
- {error,{badarg7,Nodes}}.
-
-%% Help function redoing the command history log at all nodes that actually
-%% started to trace. Note that we do not modify the return value which will be
-%% given to the caller just because we decide not to redo commands. The user
-%% must conclude him self from the inviso return value that commands were not
-%% redone at a particular node.
-h_reinitiate_session_chl(local_runtime,LD) ->
- h_reinitiate_session_chl([local_runtime],LD);
-h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
- case get_state_nodes(Node,NodesD) of
- {tracing,running} -> % Only case when we shall redo!
- h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
- _ -> % No redo of chl in other cases.
- h_reinitiate_session_chl(Rest,LD)
- end;
-h_reinitiate_session_chl([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% restore_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting a session (init tracing) and redoes the history
-%% found in CHL.
-h_restore_session(MoreTDGargs,LD) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
- {ok,{SessionNr,ReturnVal,NewLD}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
- {ok,{SessionNr,ReturnVal,NewLD2}};
- {error,Reason} -> % Risk of out of control.
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% stop_session
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping tracing at tracing nodes.
-h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case h_stop_session_2(CNode,NodesD) of
- {ok,Result} ->
- {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
- {error,Reason} ->
- {error,Reason}
- end.
-
-h_stop_session_2(undefined,NodesD) -> % The non distributed case.
- case get_tracing_nodes(NodesD) of
- {up,{inactive,_}} -> % Already not tracing!
- {ok,[]};
- {up,_} ->
- case inviso:stop_tracing() of
- {ok,_State} ->
- {ok,[ok]};
- {error,no_response} ->
- {ok,[]};
- {error,Reason} ->
- {error,Reason}
- end;
- down ->
- {ok,[]}
- end;
-h_stop_session_2(CNode,NodesD) ->
- Nodes=get_tracing_nodes(NodesD),
- case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
- {ok,NodeResults} ->
- {ok,lists:map(fun({N,{ok,_}})->{N,ok};
- (NodeError)->NodeError
- end,
- NodeResults)};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing any trace flags, trace patterns and meta trace patterns
-%% at Nodes. This will cause the nodes to become "fresh".
-h_reset_nodes(local_runtime,_CNode) ->
- inviso:clear([keep_log_files]);
-h_reset_nodes(Nodes,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
- {ok,NodeResults} ->
- {ok,NodeResults};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% atc
-%% -----------------------------------------------------------------------------
-
-%% Function handling ativating a trace case. Trace cases that do not have a
-%% particular on/off handling (but just on in some scense) are handled here too.
-%% The trace case is entered into the Command History Log.
-%% Note that the trace case can not be executed at this node but must be
-%% executed where the inviso control component is.
-%% Further it is possible to either activated the tracecase for all running and
-%% tracing nodes, or just for a specified list of nodes.
-%% TC=tracecase_name(),
-%% Id=term(), identifiying this usage so we can turn it off later.
-%% Vars=list(), list of variable-value bindnings.
-h_atc(TC,Id,Vars,LD) ->
- h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
-
-h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- if
+ {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
+ case get_state_nodes(N,NodesD) of
+ {inactive,running} -> % Only ok case.
+ case rpc:call(CNode,inviso,ctp_all,[[N]]) of
+ {ok,[{N,ok}]} ->
+ {[N|Nodes2],NodesErr};
+ {ok,[{N,{error,Reason}}]} ->
+ {Nodes2,[{N,{error,Reason}}|NodesErr]};
+ {error,Reason} ->
+ {Nodes2,[{N,{error,Reason}}|NodesErr]};
+ {badrpc,Reason} ->
+ {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
+ end;
+ {_,suspended} ->
+ {Nodes2,[{N,{error,suspended}}|NodesErr]};
+ down ->
+ {Nodes2,[{N,{error,down}}|NodesErr]};
+ false ->
+ {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
+ _ ->
+ {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
+ end
+ end,
+ {[],[]},
+ Nodes)};
+h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
+ {error,{badarg7,Nodes}}.
+
+%% Help function redoing the command history log at all nodes that actually
+%% started to trace. Note that we do not modify the return value which will be
+%% given to the caller just because we decide not to redo commands. The user
+%% must conclude him self from the inviso return value that commands were not
+%% redone at a particular node.
+h_reinitiate_session_chl(local_runtime,LD) ->
+ h_reinitiate_session_chl([local_runtime],LD);
+h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
+ case get_state_nodes(Node,NodesD) of
+ {tracing,running} -> % Only case when we shall redo!
+ h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
+ _ -> % No redo of chl in other cases.
+ h_reinitiate_session_chl(Rest,LD)
+ end;
+h_reinitiate_session_chl([],LD) ->
+ LD.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% restore_session
+%% -----------------------------------------------------------------------------
+
+%% Help function starting a session (init tracing) and redoes the history
+%% found in CHL.
+h_restore_session(MoreTDGargs,LD) ->
+ DateTime=calendar:universal_time(),
+ {M,F,Args}=LD#ld.tdg,
+ TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
+ case h_start_session(M,F,TDGargs,LD) of
+ {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
+ {ok,{SessionNr,ReturnVal,NewLD}};
+ {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
+ NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
+ {ok,{SessionNr,ReturnVal,NewLD2}};
+ {error,Reason} -> % Risk of out of control.
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% stop_session
+%% -----------------------------------------------------------------------------
+
+%% Help function stopping tracing at tracing nodes.
+h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
+ case h_stop_session_2(CNode,NodesD) of
+ {ok,Result} ->
+ {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+h_stop_session_2(undefined,NodesD) -> % The non distributed case.
+ case get_tracing_nodes(NodesD) of
+ {up,{inactive,_}} -> % Already not tracing!
+ {ok,[]};
+ {up,_} ->
+ case inviso:stop_tracing() of
+ {ok,_State} ->
+ {ok,[ok]};
+ {error,no_response} ->
+ {ok,[]};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ down ->
+ {ok,[]}
+ end;
+h_stop_session_2(CNode,NodesD) ->
+ Nodes=get_tracing_nodes(NodesD),
+ case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
+ {ok,NodeResults} ->
+ {ok,lists:map(fun({N,{ok,_}})->{N,ok};
+ (NodeError)->NodeError
+ end,
+ NodeResults)};
+ {error,Reason} ->
+ {error,Reason};
+ {badrpc,Reason} ->
+ {error,{inviso_control_node_error,Reason}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function removing any trace flags, trace patterns and meta trace patterns
+%% at Nodes. This will cause the nodes to become "fresh".
+h_reset_nodes(local_runtime,_CNode) ->
+ inviso:clear([keep_log_files]);
+h_reset_nodes(Nodes,CNode) ->
+ case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
+ {ok,NodeResults} ->
+ {ok,NodeResults};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% atc
+%% -----------------------------------------------------------------------------
+
+%% Function handling ativating a trace case. Trace cases that do not have a
+%% particular on/off handling (but just on in some scense) are handled here too.
+%% The trace case is entered into the Command History Log.
+%% Note that the trace case can not be executed at this node but must be
+%% executed where the inviso control component is.
+%% Further it is possible to either activated the tracecase for all running and
+%% tracing nodes, or just for a specified list of nodes.
+%% TC=tracecase_name(),
+%% Id=term(), identifiying this usage so we can turn it off later.
+%% Vars=list(), list of variable-value bindnings.
+h_atc(TC,Id,Vars,LD) ->
+ h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
+
+h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
+ case find_id_chl(TC,Id,CHL) of
+ activating -> % Already started.
+ {error,activating};
+ stopping -> % Not yet stopped.
+ {error,deactivating};
+ false ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} -> % Such a trace case exists.
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} -> % Necessary vars exists in Vars.
+ if
is_list(Nodes) -> % Nodes predefined.
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
- true -> % Use all tracing and running nodes.
- Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-
-h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
- case exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} -> % Trace cases have no return values.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_atc
-%% -----------------------------------------------------------------------------
-
-h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% rtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling running a trace case without marking it as activated. It
-%% is in the history mearly indicated as activated
-h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% dtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling turning a trace case off. The trace case must be registered
-%% as having an off mechanism. If it has an off mechanism and was previously entered
-%% into the Command History Log and is done with its activation phase, it will be
-%% executed and removed from the CHL.
-h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} ->
- NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_dtc
-%% -----------------------------------------------------------------------------
-
-h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
- NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} -> % Script fault.
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- false ->
- {error,no_deactivation}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% inviso
-%% -----------------------------------------------------------------------------
-
-%% Function executing one inviso command. The returnvalue from the inviso
-%% function call will be the return value to the client. The command is
-%% entered into the history command log.
-%% Note that the inviso call may have to be done at another node, dictated
-%% by the c_node field. Further, if the module name is not an atom it is
-%% most likely a regexp, which must be expanded at the regexp_node. Note
-%% this is only relevant for tp and tpl.
-h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
- Arity=length(Args),
- case check_proper_inviso_call(Cmd,Arity) of
- {true,RegExpFlag} -> % Yes it is an inviso call.
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
- {ok,Result} ->
- case check_inviso_call_to_history(Cmd,Arity) of
- true -> % This function shall be added to chl.
- {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
- false -> % Do not add it.
- {ok,{Result,LD}}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Not an inviso function.
- {error,invalid_function_name}
- end.
-
-h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
- case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
- {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
- {error,{'EXIT',Reason}};
- Result ->
- {ok,Result}
- end;
-h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
- case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
- {ok,NewArgs} ->
- case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- {error,{badrpc,Reason}} -> % Includes runtime failure.
- {error,{badrpc,Reason}};
- Result ->
- {ok,Result}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reactivate
-%% -----------------------------------------------------------------------------
-
-h_reactivate(_Node,undefined) -> % The non-distributed case.
- case inviso:cancel_suspension() of
- ok ->
- ok;
- {error,Reason} ->
- {error,Reason}
- end;
-h_reactivate(Node,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
- {ok,[{Node,ok}]} ->
- ok;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% save_history
-%% -----------------------------------------------------------------------------
-
-h_save_history(HDir,Dir,FileName,SortedLog) ->
- Dir0=
- if
+ h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
+ true -> % Use all tracing and running nodes.
+ Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
+ h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
+ end;
+ {error,Reason} -> % Variable def missing.
+ {error,Reason}
+ end;
+ false ->
+ {error,unknown_tracecase}
+ end;
+ {ok,_Bindings} -> % Already activated and running.
+ {error,already_started}
+ end.
+
+h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
+ {ok,ProcH} = exec_trace_case_on(CNode,TraceCase,Bindings,Nodes),
+ %% Trace cases have no return values.
+ NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
+ {ok,LD#ld{chl=NewCHL}}.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% sync_atc
+%% -----------------------------------------------------------------------------
+
+h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case find_id_chl(TC,Id,CHL) of
+ activating -> % Already started.
+ {error,activating};
+ stopping -> % Not yet stopped.
+ {error,deactivating};
+ false ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} -> % Such a trace case exists.
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} -> % Necessary vars exists in Vars.
+ {ok,TcFName}=get_tc_activate_fname(TraceCase),
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
+ RpcNode=get_rpc_nodename(CNode),
+ case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
+ {ok,Value} ->
+ FakeProcH=make_ref(),
+ NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
+ NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
+ {ok,LD#ld{chl=NewCHL2},Value};
+ {error,Reason} ->
+ {error,{faulty_tracecase,{TcFName,Reason}}};
+ {badrpc,Reason} ->
+ {error,{badrpc,Reason}}
+ end;
+ {error,Reason} -> % Variable def missing.
+ {error,Reason}
+ end;
+ false ->
+ {error,unknown_tracecase}
+ end;
+ {ok,_Bindings} -> % Already activated and running.
+ {error,already_started}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% rtc
+%% -----------------------------------------------------------------------------
+
+%% Function handling running a trace case without marking it as activated. It
+%% is in the history mearly indicated as activated
+h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} -> % Such a trace case exists.
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} -> % Necessary vars exists in Vars.
+ {ok,TcFName}=get_tc_activate_fname(TraceCase),
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
+ RpcNode=get_rpc_nodename(CNode),
+ case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
+ {ok,Value} ->
+ {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
+ {error,Reason} ->
+ {error,{faulty_tracecase,{TcFName,Reason}}};
+ {badrpc,Reason} ->
+ {error,{badrpc,Reason}}
+ end;
+ {error,Reason} -> % Variable def missing.
+ {error,Reason}
+ end;
+ false ->
+ {error,unknown_tracecase}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% dtc
+%% -----------------------------------------------------------------------------
+
+%% Function handling turning a trace case off. The trace case must be registered
+%% as having an off mechanism. If it has an off mechanism and was previously entered
+%% into the Command History Log and is done with its activation phase, it will be
+%% executed and removed from the CHL.
+h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case find_id_chl(TC,Id,CHL) of
+ {ok,Bindings} -> % Yes, we have turned it on before.
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} ->
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
+ {ok,ProcH} ->
+ NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
+ {ok,LD#ld{chl=NewCHL}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ false -> % Strange, Id ok but no such trace case.
+ {error,unknown_tracecase}
+ end;
+ false -> % Not previously turned on.
+ {error,unknown_id};
+ activating ->
+ {error,activating};
+ stopping ->
+ {error,already_deactivating}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% sync_dtc
+%% -----------------------------------------------------------------------------
+
+h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case find_id_chl(TC,Id,CHL) of
+ {ok,Bindings} -> % Yes, we have turned it on before.
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} ->
+ case get_tc_deactivate_fname(TraceCase) of
+ {ok,TcFName} ->
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
+ RpcNode=get_rpc_nodename(CNode),
+ case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
+ {ok,Value} ->
+ FakeProcH=make_ref(),
+ NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
+ NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
+ {ok,LD#ld{chl=NewCHL2},Value};
+ {error,Reason} -> % Script fault.
+ {error,{faulty_tracecase,{TcFName,Reason}}};
+ {badrpc,Reason} ->
+ {error,{badrpc,Reason}}
+ end;
+ false ->
+ {error,no_deactivation}
+ end;
+ false -> % Strange, Id ok but no such trace case.
+ {error,unknown_tracecase}
+ end;
+ false -> % Not previously turned on.
+ {error,unknown_id};
+ activating ->
+ {error,activating};
+ stopping ->
+ {error,already_deactivating}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% inviso
+%% -----------------------------------------------------------------------------
+
+%% Function executing one inviso command. The returnvalue from the inviso
+%% function call will be the return value to the client. The command is
+%% entered into the history command log.
+%% Note that the inviso call may have to be done at another node, dictated
+%% by the c_node field. Further, if the module name is not an atom it is
+%% most likely a regexp, which must be expanded at the regexp_node. Note
+%% this is only relevant for tp and tpl.
+h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
+ Arity=length(Args),
+ case check_proper_inviso_call(Cmd,Arity) of
+ {true,RegExpFlag} -> % Yes it is an inviso call.
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
+ {ok,Result} ->
+ case check_inviso_call_to_history(Cmd,Arity) of
+ true -> % This function shall be added to chl.
+ {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
+ false -> % Do not add it.
+ {ok,{Result,LD}}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ false -> % Not an inviso function.
+ {error,invalid_function_name}
+ end.
+
+h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
+ case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
+ {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
+ {error,{'EXIT',Reason}};
+ Result ->
+ {ok,Result}
+ end;
+h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
+ case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
+ {ok,NewArgs} ->
+ case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
+ {'EXIT',Reason} ->
+ {error,{'EXIT',Reason}};
+ {error,{badrpc,Reason}} -> % Includes runtime failure.
+ {error,{badrpc,Reason}};
+ Result ->
+ {ok,Result}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% reactivate
+%% -----------------------------------------------------------------------------
+
+h_reactivate(_Node,undefined) -> % The non-distributed case.
+ case inviso:cancel_suspension() of
+ ok ->
+ ok;
+ {error,Reason} ->
+ {error,Reason}
+ end;
+h_reactivate(Node,CNode) ->
+ case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
+ {ok,[{Node,ok}]} ->
+ ok;
+ {ok,[{Node,{error,Reason}}]} ->
+ {error,Reason};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% save_history
+%% -----------------------------------------------------------------------------
+
+h_save_history(HDir,Dir,FileName,SortedLog) ->
+ Dir0=
+ if
is_list(HDir) -> % There is a history dir specified.
- HDir; % Use it then.
- true ->
- Dir % Else use the tool dir.
- end,
- case catch make_absolute_path(FileName,Dir0) of
+ HDir; % Use it then.
+ true ->
+ Dir % Else use the tool dir.
+ end,
+ case catch make_absolute_path(FileName,Dir0) of
AbsFileName when is_list(AbsFileName) ->
- Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
- case file:write_file(AbsFileName,term_to_binary(Log2)) of
- ok ->
- {ok,AbsFileName};
- {error,Reason} ->
- {error,{write_file,Reason}}
- end;
- {'EXIT',_Reason} ->
- {error,{bad_filename,FileName}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% get_autostart_data
-%% -----------------------------------------------------------------------------
-
-%% Help function building the structures used when exporting autostart information
-%% from the tool. Note that we remove the tool-dependency and insert the one
-%% specify in the get_autostart_data call.
-h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
- Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
-
+ Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
+ case file:write_file(AbsFileName,term_to_binary(Log2)) of
+ ok ->
+ {ok,AbsFileName};
+ {error,Reason} ->
+ {error,{write_file,Reason}}
+ end;
+ {'EXIT',_Reason} ->
+ {error,{bad_filename,FileName}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% get_autostart_data
+%% -----------------------------------------------------------------------------
+
+%% Help function building the structures used when exporting autostart information
+%% from the tool. Note that we remove the tool-dependency and insert the one
+%% specify in the get_autostart_data call.
+h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
+ CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
+ Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
+ Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
+ {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
+
h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when is_list(Nodes) ->
- {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
-h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
- {error,{badarg,Nodes}}.
-
-h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
- Opts0=start_runtime_components_mk_opts(Node,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
- h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
-h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% flush
-%% -----------------------------------------------------------------------------
-
-h_flush(undefined,_Nodes) ->
- inviso:flush();
-h_flush(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% tc_executer_reply
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its activation phase and
-%% shall now be marked in the Command History Log as running.
-h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {activating,{TC,Id}} ->
- case Result of
- {ok,Value} -> % The trace case is successful activated.
- {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
- {error,_} -> % Then pretend it never happend :-)
- {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
- end;
- _ -> % Where did this come from?
- {ok,LD} % Well just ignore it then.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its stopping phase and
-%% shall now be nulled in the Command History Log (meaning that it will not
-%% be repeated in the event of a reactivation).
-h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {stopping,{TC,Id}} ->
- case Result of
- {ok,_Result} -> % _Result is returned from the tracecase.
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
- {error,_} -> % This is difficult, is it still active?
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
- end;
- _ -> % Strange.
- {ok,LD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Terminate.
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping the inviso control component. Does not return
-%% anything significant.
-stop_inviso_at_c_node(undefined) -> % Non distributed case.
- inviso:stop();
-stop_inviso_at_c_node(CNode) ->
- rpc:call(CNode,inviso,stop,[]).
-%% -----------------------------------------------------------------------------
-
-%% Help function that removes all trace patterns from the nodes that are not
-%% marked as such were patterns shall be left after stopping of inviso.
-%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
-%% 'ok' is returned incase of success, ot 'patterns_untouched'.
-remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
- case KeepNodes of
- undefined -> % No, remove patterns from localruntime.
- inviso:ctp_all();
- _ ->
- patterns_untouched
- end;
-remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
- Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
- case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
- {ok,NodeResults} ->
- F=fun(N) ->
- case lists:member(N,KeepNodes) of
- true ->
- {N,patterns_untouched};
- false ->
- case lists:keysearch(N,1,NodeResults) of
- {value,Result} ->
- Result; % {Node,ok}
- false -> % Extremely strange.
- {N,{error,general_error}}
- end
- end
- end,
- {ok,lists:map(F,Nodes)};
- {error,{badrpc,Reason}} ->
- {error,{inviso_control_node_error,Reason}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Second level help functions.
-%% =============================================================================
-
-%% Help function building a reply to a reconnection call based on which nodes
-%% where asked to be reconnected and which of those are actually now working.
-%% We actually make an effort to serve the return value in the same order as the
-%% nodes were mentioned in the original call (Nodes).
-build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {error,down};
- {State,Status} ->
- {ok,{State,Status}}
- end;
-build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
- NodesErr;
-build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
- case lists:member(Node,Nodes2) of
- true -> % Ok, look in the #ld.nodes.
- case get_state_nodes(Node,NodesD) of
- down -> % Somekind of failure, still down.
- [{Node,{error,down}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
- {State,Status} -> % {State,Status}
- [{Node,{ok,{State,Status}}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
- false -> % Error already from the beginning.
- {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
- [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
-build_reconnect_nodes_reply([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function building a return value to reinitiate_session. Nodes contains
-%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
-%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
-build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
- {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
-build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
- NodeResult;
-build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
- NodesErr.
-build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
- case lists:keysearch(Node,1,NodesErr) of
- {value,{_,Error}} ->
- [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
- false ->
- case lists:keysearch(Node,1,NodeResults) of
- {value,Value} ->
- [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
- end
- end;
-build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a history log where stop and stopping entries have
-%% been removed. Further all tracecase log entries must be set to running since
-%% there can not be such a thing as an activating tracecase stored away in a
-%% saved historyfile!
-%% We must also take away any #Ref.
-build_saved_history_data(SortedLog) ->
- CleanedLog=
- lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
- (_) -> true
- end,
- SortedLog),
- lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
- ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
- ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
- ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
- end,
- CleanedLog).
-%% -----------------------------------------------------------------------------
-
-%% This help function builds the AutoStartData structure which is returned from
-%% get_austostart_data. An AutoStartData structure is a list of trace-files and
-%% inviso commands. The order is significant since it is the idea that doing
-%% the trace case files and inviso commands in that order will bring a node to
-%% a certain state in a trace perspective.
-%% Returns {ok,AutoStartData} or {error,Reason}
-build_autostart_data(SortedLog,TCdict) ->
- build_autostart_data_2(SortedLog,TCdict,[]).
-
-build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
- build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
-build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
- build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
-build_autostart_data_2([],_TCdict,Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function placing the filename in the AutoStartData structure.
-build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
- {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
- {ok,FName}=get_tc_activate_fname(TC),
- build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
-%% -----------------------------------------------------------------------------
-
-%% Help function generating tracerdata to init inviso tracing. The generation
-%% is done by the TracerDataGenerator, TDG, function.
-%% Individual tracerdata is generated for each node in Nodes.
-%% Returns {ok,TracerData} or {error,Reason}.
-call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
- case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerData ->
- {ok,TracerData}
- end;
-call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
- case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerList ->
- {ok,TracerList}
- end.
-
-call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
- [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
- call_tracer_data_generator_2(M,F,TDGargs,Rest)];
-call_tracer_data_generator_2(_,_,_,[]) ->
- [].
-
-call_tracer_data_generator_3(M,F,TDGargs,Node) ->
- apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
-
-%% This function creates the arguments that the tracer data generator function
-%% accepts (in an apply call). The reason for making it a sepparate function is
-%% that the arguments are constructed in more situations than just when actually
-%% doing the apply. By having a function it will become obvious where to change
-%% should the arguments change.
-call_tracer_data_generator_mkargs(Node,TDGargs) ->
- inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
-%% -----------------------------------------------------------------------------
-
-%% This function acts as standard options generator function. That is returning
-%% the options argument to inviso:add_node/3. Note that this function must not
-%% return the dependency part of that option.
-std_options_generator(_Node) ->
- []. % No particular options(!)
-%% -----------------------------------------------------------------------------
-
-
-%% Help function checking that Vars contains a binding for every variable
-%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
-%% is disregarded, since it is always added by the inviso_tool.
-%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
-%% according to file:eval functionality.
-check_bindings(Vars,TraceCase) ->
- case catch check_bindings_2(Vars,
- get_tc_varnames(TraceCase),
- erl_eval:new_bindings()) of
- {'EXIT',_Reason} ->
- {error,variable_error};
- {error,Reason} -> % Missing a bindning.
- {error,Reason};
- {ok,Bindings} ->
- {ok,Bindings}
- end.
-
-check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
- check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
-check_bindings_2(Vars,[VarName|Rest],Bindings) ->
- case lists:keysearch(VarName,1,Vars) of
- {value,{_,Val}} ->
- check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
- false -> % Mandatory variable missing.
- {error,{missing_variable,VarName}} % Quite here then.
- end;
-check_bindings_2(_,[],Bindings) ->
- {ok,Bindings}.
-%% -----------------------------------------------------------------------------
-
-%% This help function checks that the command the user tries to do is amongst
-%% the inviso API. It at the same time returns what kind of command it is.
-%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
-%% needs to have its argument modified by module regexp expansion or not.
-check_proper_inviso_call(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMDS) of
- true -> % It is part of inviso API.
- {true,check_proper_inviso_call_regexp(Cmd,Arity)};
- false ->
- false
- end.
-
-%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
-check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking if this inviso command shall be added to the command
-%% history log. Returns true or false.
-check_inviso_call_to_history(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function traversing the arguments and expanding module names stated
-%% as regular expressions. This means that the resulting arguments may be longer
-%% than the orginal ones.
-%% When we run this function it has been determined that we are a distributed
-%% system.
-%% Also note that if there are no regexps in Args, no regexpansion will be
-%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
-%% If RegExpNode is unavailable the nodes found in Nodes will be used until
-%% one that works is found.
-expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
- {ok,Args};
-expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
- case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
+ {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
+h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
+ {error,{badarg,Nodes}}.
+
+h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
+ CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
+ Opts0=start_runtime_components_mk_opts(Node,OptsG),
+ Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
+ [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
+ h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
+h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% flush
+%% -----------------------------------------------------------------------------
+
+h_flush(undefined,_Nodes) ->
+ inviso:flush();
+h_flush(CNode,Nodes) ->
+ inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% tc_executer_reply
+%% -----------------------------------------------------------------------------
+
+%% Function handling that a trace case has completed its activation phase and
+%% shall now be marked in the Command History Log as running.
+h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
+ case find_tc_executer_chl(ProcH,CHL) of
+ {activating,{TC,Id}} ->
+ case Result of
+ {ok,Value} -> % The trace case is successful activated.
+ {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
+ {error,_} -> % Then pretend it never happend :-)
+ {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
+ end;
+ _ -> % Where did this come from?
+ {ok,LD} % Well just ignore it then.
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Function handling that a trace case has completed its stopping phase and
+%% shall now be nulled in the Command History Log (meaning that it will not
+%% be repeated in the event of a reactivation).
+h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
+ case find_tc_executer_chl(ProcH,CHL) of
+ {stopping,{TC,Id}} ->
+ case Result of
+ {ok,_Result} -> % _Result is returned from the tracecase.
+ {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
+ {error,_} -> % This is difficult, is it still active?
+ {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
+ end;
+ _ -> % Strange.
+ {ok,LD}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Terminate.
+%% -----------------------------------------------------------------------------
+
+%% Help function stopping the inviso control component. Does not return
+%% anything significant.
+stop_inviso_at_c_node(undefined) -> % Non distributed case.
+ inviso:stop();
+stop_inviso_at_c_node(CNode) ->
+ rpc:call(CNode,inviso,stop,[]).
+%% -----------------------------------------------------------------------------
+
+%% Help function that removes all trace patterns from the nodes that are not
+%% marked as such were patterns shall be left after stopping of inviso.
+%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
+%% 'ok' is returned incase of success, ot 'patterns_untouched'.
+remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
+ case KeepNodes of
+ undefined -> % No, remove patterns from localruntime.
+ inviso:ctp_all();
+ _ ->
+ patterns_untouched
+ end;
+remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
+ Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
+ case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
+ {ok,NodeResults} ->
+ F=fun(N) ->
+ case lists:member(N,KeepNodes) of
+ true ->
+ {N,patterns_untouched};
+ false ->
+ case lists:keysearch(N,1,NodeResults) of
+ {value,Result} ->
+ Result; % {Node,ok}
+ false -> % Extremely strange.
+ {N,{error,general_error}}
+ end
+ end
+ end,
+ {ok,lists:map(F,Nodes)};
+ {error,{badrpc,Reason}} ->
+ {error,{inviso_control_node_error,Reason}};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% =============================================================================
+%% Second level help functions.
+%% =============================================================================
+
+%% Help function building a reply to a reconnection call based on which nodes
+%% where asked to be reconnected and which of those are actually now working.
+%% We actually make an effort to serve the return value in the same order as the
+%% nodes were mentioned in the original call (Nodes).
+build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
+ case get_state_nodes(local_runtime,NodesD) of
+ down ->
+ {error,down};
+ {State,Status} ->
+ {ok,{State,Status}}
+ end;
+build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
+ NodesErr;
+build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
+ case lists:member(Node,Nodes2) of
+ true -> % Ok, look in the #ld.nodes.
+ case get_state_nodes(Node,NodesD) of
+ down -> % Somekind of failure, still down.
+ [{Node,{error,down}}|
+ build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
+ {State,Status} -> % {State,Status}
+ [{Node,{ok,{State,Status}}}|
+ build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
+ end;
+ false -> % Error already from the beginning.
+ {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
+ [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
+ end;
+build_reconnect_nodes_reply([],_,_,_) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Help function building a return value to reinitiate_session. Nodes contains
+%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
+%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
+build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
+ {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
+build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
+ NodeResult;
+build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
+ NodesErr.
+build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
+ case lists:keysearch(Node,1,NodesErr) of
+ {value,{_,Error}} ->
+ [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
+ false ->
+ case lists:keysearch(Node,1,NodeResults) of
+ {value,Value} ->
+ [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
+ end
+ end;
+build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Help function returning a history log where stop and stopping entries have
+%% been removed. Further all tracecase log entries must be set to running since
+%% there can not be such a thing as an activating tracecase stored away in a
+%% saved historyfile!
+%% We must also take away any #Ref.
+build_saved_history_data(SortedLog) ->
+ CleanedLog=
+ lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
+ (_) -> true
+ end,
+ SortedLog),
+ lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
+ ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
+ ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
+ ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
+ end,
+ CleanedLog).
+%% -----------------------------------------------------------------------------
+
+%% This help function builds the AutoStartData structure which is returned from
+%% get_austostart_data. An AutoStartData structure is a list of trace-files and
+%% inviso commands. The order is significant since it is the idea that doing
+%% the trace case files and inviso commands in that order will bring a node to
+%% a certain state in a trace perspective.
+%% Returns {ok,AutoStartData} or {error,Reason}
+build_autostart_data(SortedLog,TCdict) ->
+ build_autostart_data_2(SortedLog,TCdict,[]).
+
+build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
+ build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
+build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
+ build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
+build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
+ build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
+build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
+ build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
+build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
+ build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
+build_autostart_data_2([],_TCdict,Accum) ->
+ {ok,lists:reverse(Accum)}.
+
+%% Help function placing the filename in the AutoStartData structure.
+build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
+ {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
+ {ok,FName}=get_tc_activate_fname(TC),
+ build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
+%% -----------------------------------------------------------------------------
+
+%% Help function generating tracerdata to init inviso tracing. The generation
+%% is done by the TracerDataGenerator, TDG, function.
+%% Individual tracerdata is generated for each node in Nodes.
+%% Returns {ok,TracerData} or {error,Reason}.
+call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
+ case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
+ {'EXIT',Reason} ->
+ {error,{'EXIT',Reason}};
+ TracerData ->
+ {ok,TracerData}
+ end;
+call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
+ case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
+ {'EXIT',Reason} ->
+ {error,{'EXIT',Reason}};
+ TracerList ->
+ {ok,TracerList}
+ end.
+
+call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
+ [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
+ call_tracer_data_generator_2(M,F,TDGargs,Rest)];
+call_tracer_data_generator_2(_,_,_,[]) ->
+ [].
+
+call_tracer_data_generator_3(M,F,TDGargs,Node) ->
+ apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
+
+%% This function creates the arguments that the tracer data generator function
+%% accepts (in an apply call). The reason for making it a sepparate function is
+%% that the arguments are constructed in more situations than just when actually
+%% doing the apply. By having a function it will become obvious where to change
+%% should the arguments change.
+call_tracer_data_generator_mkargs(Node,TDGargs) ->
+ inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
+%% -----------------------------------------------------------------------------
+
+%% This function acts as standard options generator function. That is returning
+%% the options argument to inviso:add_node/3. Note that this function must not
+%% return the dependency part of that option.
+std_options_generator(_Node) ->
+ []. % No particular options(!)
+%% -----------------------------------------------------------------------------
+
+
+%% Help function checking that Vars contains a binding for every variable
+%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
+%% is disregarded, since it is always added by the inviso_tool.
+%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
+%% according to file:eval functionality.
+check_bindings(Vars,TraceCase) ->
+ case catch check_bindings_2(Vars,
+ get_tc_varnames(TraceCase),
+ erl_eval:new_bindings()) of
+ {'EXIT',_Reason} ->
+ {error,variable_error};
+ {error,Reason} -> % Missing a bindning.
+ {error,Reason};
+ {ok,Bindings} ->
+ {ok,Bindings}
+ end.
+
+check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
+ check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
+check_bindings_2(Vars,[VarName|Rest],Bindings) ->
+ case lists:keysearch(VarName,1,Vars) of
+ {value,{_,Val}} ->
+ check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
+ false -> % Mandatory variable missing.
+ {error,{missing_variable,VarName}} % Quite here then.
+ end;
+check_bindings_2(_,[],Bindings) ->
+ {ok,Bindings}.
+%% -----------------------------------------------------------------------------
+
+%% This help function checks that the command the user tries to do is amongst
+%% the inviso API. It at the same time returns what kind of command it is.
+%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
+%% needs to have its argument modified by module regexp expansion or not.
+check_proper_inviso_call(Cmd,Arity) ->
+ case lists:member({Cmd,Arity},?INVISO_CMDS) of
+ true -> % It is part of inviso API.
+ {true,check_proper_inviso_call_regexp(Cmd,Arity)};
+ false ->
+ false
+ end.
+
+%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
+check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
+check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
+check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
+check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
+check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
+check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
+check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
+check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
+check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
+check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
+check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
+ false.
+%% -----------------------------------------------------------------------------
+
+%% Help function checking if this inviso command shall be added to the command
+%% history log. Returns true or false.
+check_inviso_call_to_history(Cmd,Arity) ->
+ case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
+ true ->
+ true;
+ false ->
+ false
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function traversing the arguments and expanding module names stated
+%% as regular expressions. This means that the resulting arguments may be longer
+%% than the orginal ones.
+%% When we run this function it has been determined that we are a distributed
+%% system.
+%% Also note that if there are no regexps in Args, no regexpansion will be
+%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
+%% If RegExpNode is unavailable the nodes found in Nodes will be used until
+%% one that works is found.
+expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
+ {ok,Args};
+expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
+ case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
NewPatternList when is_list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
- case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
+ {ok,[NewPatternList]};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
+ case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
NewPatternList when is_list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
- expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
-
-
+ {ok,[NewPatternList]};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
+ expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
+expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
+ expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
+expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
+ expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
+
+
expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
- [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
+ [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
- {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {error,_Reason} ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity,MS,Opts}|
- expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes).
-
+ case inviso_tool_lib:expand_module_names([RegExpNode],
+ M,
+ [{expand_only_at,RegExpNode}]) of
+ {singlenode_expansion,Modules} ->
+ expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
+ {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
+ case Nodes of
+ [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
+ expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
+ [] -> % No more nodes to choose from.
+ throw({error,no_available_regexpnode})
+ end;
+ {error,_Reason} ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes)
+ end;
+expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
+expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
+ [].
+
+expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
+ [{M,F,Arity,MS,Opts}|
+ expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
+expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes).
+
expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
- [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
+ [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,badrpc} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_ctp([{M,F,Arity}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {singlenode_expansion,Modules} ->
- expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
- {error,_Reason} ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
-%% -----------------------------------------------------------------------------
-
-
-
-%% Help function running the activation of a trace case. Note that this must
-%% be done at the inviso control component's Erlang node *and* that it must be
-%% done in its own process since there is no telling for how long a trace case
-%% may run.
-%% Returns {ok,ActivationHandler}.
-exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- activating)}.
-
-%% Help function running the deactivation of a trace case.
-exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} -> % There is a deactivation.
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- stopping)};
- false ->
- {error,no_deactivation}
- end.
-
-exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
- if
- CNode==undefined -> % The non distributed case.
- spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
- true ->
- spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
- end.
-
-%% This function is run in its own process and is responsible for executing
-%% the trace case.
-tc_executer(TcFName,Bindings,Phase,Parent) ->
- case catch file:script(TcFName,Bindings) of
- {ok,Value} ->
- tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
- {'EXIT',Reason} ->
- tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
- Error ->
- tc_executer_reply(Parent,{Phase,self(),Error})
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a reactivator process redoing command history at
-%% Node. It also updates the loopdata to indicate that Node is now in state
-%% reactivating. It is a good idea to only handle one node per reactivator process.
-%% This because if the node terminates and comes back up, the reactivator must be
-%% stopped.
-redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
- P=start_reactivator(Node,CNode,TCdict,CHL),
- LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
- reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
-
-%% Help function starting a reactivator process replaying the command history log.
-%% Returns a pid of the reactivator process.
-start_reactivator(Node,CNode,TCdict,CHL) ->
- UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
- if
- CNode==undefined -> % The non-distributed case.
- spawn_link(?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]]);
- true ->
- spawn_link(CNode,
- ?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]])
- end.
-
-%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
-%% commands one by one. We wait until one command is finished until we do the
-%% next. Commands marked as nullified are not performed. In fact when a command
-%% is nullified only the stop will be found in the CHL. Its activation will be
-%% removed.
-reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
- SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
- Log=reactivator_skip_log_entries(SortedLog,StartCounter),
- case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
- done ->
- true; % Simply terminate the reactivator then.
- {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
- reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
- end.
-
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
- reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{M,F,Args,_Ref},NextC}|Rest]) ->
- reactivator_executer_cmd(Node,M,F,Args),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
- case lists:member({TCname,Id},DoneCases) of
- true -> % We have activated it, must stop then.
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,_,FNameOff}} ->
- reactivator_executer_tc(Node,Bindings,FNameOff),
- NewDoneCases=lists:delete({TCname,Id},DoneCases),
- reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
- {ok,_} -> % No stop-filename, strange!
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Even stranger, does not exist!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
- false -> % Never activated in the first place.
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
-%% Done all log entries found this lap. See if there are more entries by now.
-reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
- case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
- done -> % No more entries in the CHL.
- done;
- {more,NewUnsortedLog} -> % Repeat the procedure
- {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
- end.
-
-%% This help function activates a tracecase.
-reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-%% Help function executing a trace case in the reactivators context. Does not
-%% return anything significant.
-reactivator_executer_tc(Node,Bindings,FileName) ->
- catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
-
-%% Help function handling trace case that are simply executed - rtc.
-reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
- catch apply(M,F,Args); % Non-distributed.
-reactivator_executer_cmd(Node,M,F,Args) ->
- catch apply(M,F,[[Node]|Args]).
-
-%% Help function returning a list of log entries missing the first entries
-%% having a counter less or equal to C1.
-reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries(Log,_) ->
- Log.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning the node name to use in an rpc call.
-get_rpc_nodename(undefined) ->
- node();
-get_rpc_nodename(CNode) ->
- CNode.
-%% -----------------------------------------------------------------------------
-
-mk_rt_tag() ->
- inviso_tool.
-%% -----------------------------------------------------------------------------
-
-is_string([C|Rest]) when C>=32, C=<255 ->
- is_string(Rest);
-is_string([]) ->
- true;
-is_string(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions for handling the configuration file.
-%% -----------------------------------------------------------------------------
-
-%% The inviso tool is configured via start arguments and/or a configuration file.
-%% Start arguments will override any definitions in a configuration file.
-%% The configuration file is pointed out by either a start argument or the
-%% inviso application parameter 'inviso_tool_config_file'.
-
-%% Help function building the internal configuration structure. Configurations
-%% in the start argument will override parameters found in a configuration file.
-fetch_configuration(Config) ->
- case fetch_config_filename(Config) of
- {ok,FName} -> % We are supposed to use a conf-file.
- case read_config_file(FName) of
- {ok,LD} -> % Managed to open a file.
- NewLD=read_config_list(LD,Config),
- {ok,NewLD};
- {error,_Reason} -> % Problem finding/opening file.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end;
- false -> % No filename specified.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end.
-
-%% Help function determining the name of the file which shall be consulted as
-%% the main configuration file.
-%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
-fetch_config_filename(Config) ->
- case catch lists:keysearch(config_file,1,Config) of
+ case inviso_tool_lib:expand_module_names([RegExpNode],
+ M,
+ [{expand_only_at,RegExpNode}]) of
+ {singlenode_expansion,Modules} ->
+ expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
+ {error,_Reason} ->
+ expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
+ end;
+expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
+expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
+ [].
+
+expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
+ [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
+expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
+ expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
+%% -----------------------------------------------------------------------------
+
+
+
+%% Help function running the activation of a trace case. Note that this must
+%% be done at the inviso control component's Erlang node *and* that it must be
+%% done in its own process since there is no telling for how long a trace case
+%% may run.
+%% Returns {ok,ActivationHandler}.
+exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
+ {ok,TcFName}=get_tc_activate_fname(TraceCase),
+ {ok,exec_trace_case_2(CNode,
+ TcFName,
+ erl_eval:add_binding('Nodes',Nodes,Bindings),
+ activating)}.
+
+%% Help function running the deactivation of a trace case.
+exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
+ case get_tc_deactivate_fname(TraceCase) of
+ {ok,TcFName} -> % There is a deactivation.
+ {ok,exec_trace_case_2(CNode,
+ TcFName,
+ erl_eval:add_binding('Nodes',Nodes,Bindings),
+ stopping)};
+ false ->
+ {error,no_deactivation}
+ end.
+
+exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
+ if
+ CNode==undefined -> % The non distributed case.
+ spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
+ true ->
+ spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
+ end.
+
+%% This function is run in its own process and is responsible for executing
+%% the trace case.
+tc_executer(TcFName,Bindings,Phase,Parent) ->
+ case catch file:script(TcFName,Bindings) of
+ {ok,Value} ->
+ tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
+ {'EXIT',Reason} ->
+ tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
+ Error ->
+ tc_executer_reply(Parent,{Phase,self(),Error})
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function which starts a reactivator process redoing command history at
+%% Node. It also updates the loopdata to indicate that Node is now in state
+%% reactivating. It is a good idea to only handle one node per reactivator process.
+%% This because if the node terminates and comes back up, the reactivator must be
+%% stopped.
+redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
+ P=start_reactivator(Node,CNode,TCdict,CHL),
+ LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
+ reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
+
+%% Help function starting a reactivator process replaying the command history log.
+%% Returns a pid of the reactivator process.
+start_reactivator(Node,CNode,TCdict,CHL) ->
+ UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
+ if
+ CNode==undefined -> % The non-distributed case.
+ spawn_link(?MODULE,
+ reactivator_executer,
+ [Node,TCdict,UnsortedLog,self(),0,[]]);
+ true ->
+ spawn_link(CNode,
+ ?MODULE,
+ reactivator_executer,
+ [Node,TCdict,UnsortedLog,self(),0,[]])
+ end.
+
+%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
+%% commands one by one. We wait until one command is finished until we do the
+%% next. Commands marked as nullified are not performed. In fact when a command
+%% is nullified only the stop will be found in the CHL. Its activation will be
+%% removed.
+reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
+ SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
+ Log=reactivator_skip_log_entries(SortedLog,StartCounter),
+ case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
+ done ->
+ true; % Simply terminate the reactivator then.
+ {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
+ reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
+ end.
+
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
+ reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
+ reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
+ reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{M,F,Args,_Ref},NextC}|Rest]) ->
+ reactivator_executer_cmd(Node,M,F,Args),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
+ case lists:member({TCname,Id},DoneCases) of
+ true -> % We have activated it, must stop then.
+ case get_tracecase_tc_dict(TCname,TCdict) of
+ {ok,{_,_,_,_,FNameOff}} ->
+ reactivator_executer_tc(Node,Bindings,FNameOff),
+ NewDoneCases=lists:delete({TCname,Id},DoneCases),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
+ {ok,_} -> % No stop-filename, strange!
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+ false -> % Even stranger, does not exist!?
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end;
+ false -> % Never activated in the first place.
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end;
+%% Done all log entries found this lap. See if there are more entries by now.
+reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
+ case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
+ done -> % No more entries in the CHL.
+ done;
+ {more,NewUnsortedLog} -> % Repeat the procedure
+ {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
+ end.
+
+%% This help function activates a tracecase.
+reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
+ case get_tracecase_tc_dict(TCname,TCdict) of
+ {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
+ {ok,{_,_,_,FNameOn,_}} ->
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
+ false -> % Strange, does not exist anylonger!?
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end.
+
+%% Help function executing a trace case in the reactivators context. Does not
+%% return anything significant.
+reactivator_executer_tc(Node,Bindings,FileName) ->
+ catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
+
+%% Help function handling trace case that are simply executed - rtc.
+reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
+ case get_tracecase_tc_dict(TCname,TCdict) of
+ {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+ {ok,{_,_,_,FNameOn,_}} ->
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+ false -> % Strange, does not exist anylonger!?
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end.
+
+reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
+ catch apply(M,F,Args); % Non-distributed.
+reactivator_executer_cmd(Node,M,F,Args) ->
+ catch apply(M,F,[[Node]|Args]).
+
+%% Help function returning a list of log entries missing the first entries
+%% having a counter less or equal to C1.
+reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
+ reactivator_skip_log_entries(Rest,C1);
+reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
+ reactivator_skip_log_entries(Rest,C1);
+reactivator_skip_log_entries(Log,_) ->
+ Log.
+%% -----------------------------------------------------------------------------
+
+%% Help function returning the node name to use in an rpc call.
+get_rpc_nodename(undefined) ->
+ node();
+get_rpc_nodename(CNode) ->
+ CNode.
+%% -----------------------------------------------------------------------------
+
+mk_rt_tag() ->
+ inviso_tool.
+%% -----------------------------------------------------------------------------
+
+is_string([C|Rest]) when C>=32, C=<255 ->
+ is_string(Rest);
+is_string([]) ->
+ true;
+is_string(_) ->
+ false.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% Functions for handling the configuration file.
+%% -----------------------------------------------------------------------------
+
+%% The inviso tool is configured via start arguments and/or a configuration file.
+%% Start arguments will override any definitions in a configuration file.
+%% The configuration file is pointed out by either a start argument or the
+%% inviso application parameter 'inviso_tool_config_file'.
+
+%% Help function building the internal configuration structure. Configurations
+%% in the start argument will override parameters found in a configuration file.
+fetch_configuration(Config) ->
+ case fetch_config_filename(Config) of
+ {ok,FName} -> % We are supposed to use a conf-file.
+ case read_config_file(FName) of
+ {ok,LD} -> % Managed to open a file.
+ NewLD=read_config_list(LD,Config),
+ {ok,NewLD};
+ Error = {error,_Reason} -> % Problem finding/opening file.
+ Error
+ end;
+ false -> % No filename specified.
+ LD=read_config_list(#ld{},Config),
+ {ok,LD}
+ end.
+
+%% Help function determining the name of the file which shall be consulted as
+%% the main configuration file.
+%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
+fetch_config_filename(Config) ->
+ case catch lists:keysearch(config_file,1,Config) of
{value,{_,FName}} when is_list(FName) ->
- {ok,FName};
- _ -> % No filename in the start argument.
- fetch_config_filename_2()
- end.
-
-fetch_config_filename_2() ->
- case application:get_env(inviso_tool_config_file) of
+ {ok,FName};
+ _ -> % No filename in the start argument.
+ fetch_config_filename_2()
+ end.
+
+fetch_config_filename_2() ->
+ case application:get_env(inviso_tool_config_file) of
{ok,FName} when is_list(FName) ->
- {ok,FName};
- _ -> % Application parameter not specified.
- false % Means no config file will be used.
- end.
-
-%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
-read_config_file(FName) ->
- case catch file:consult(FName) of
- {ok,Terms} ->
- {ok,read_config_list(#ld{},Terms)};
- {error,Reason} ->
- {error,{file_consult,Reason}};
- {'EXIT',Reason} ->
- {error,{failure,Reason}}
- end.
-
-%% Help function traversing the Terms list entering known tag-values into #ld.
-read_config_list(LD,Terms) ->
- LD1=read_config_list_2(LD,Terms,nodes),
- LD2=read_config_list_2(LD1,Terms,c_node),
- LD3=read_config_list_2(LD2,Terms,regexp_node),
- LD4=read_config_list_2(LD3,Terms,tc_def_file),
- LD6=read_config_list_2(LD4,Terms,tdg),
- LD8=read_config_list_2(LD6,Terms,debug),
- LD10=read_config_list_2(LD8,Terms,initial_tcs),
- LD11=read_config_list_2(LD10,Terms,dir),
- _LD12=read_config_list_2(LD11,Terms,optg).
-
-read_config_list_2(LD,Terms,Tag) ->
- case catch lists:keysearch(Tag,1,Terms) of
- {value,{_,Value}} ->
- update_ld_record(LD,Tag,Value);
- _ ->
- LD % Tag not found in Terms (or error!)
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function updating a named field in a record. Returns a new record. Note that
-%% this function must be maintained due the fact that field names are removed
-%% at compile time.
-update_ld_record(LD,nodes,Value) when is_record(LD,ld) ->
- case mk_nodes(Value) of
- {ok,NodesD} ->
- LD#ld{nodes=NodesD};
- error ->
- LD
- end;
-update_ld_record(LD,Tag,Value) when is_record(LD,ld) ->
- Index=
- case Tag of
- c_node -> % atom()
- #ld.c_node;
- regexp_node -> % atom()
- #ld.regexp_node;
- tc_def_file -> % string()
- #ld.tc_def_file;
- initial_tcs -> % [{TCname,VarList},...]
- #ld.initial_tcs;
- history_dir -> % string()
- #ld.history_dir;
- debug -> % true | false
- #ld.debug;
- dir -> % string()
- #ld.dir;
- optg -> % {Mod,Func,Args}
- #ld.optg;
- tdg -> % {Mod,Func,Args}
- #ld.tdg;
- keep_nodes -> % [Nodes,...]
- #ld.keep_nodes
- end,
- setelement(Index,LD,Value). % Cheeting!
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which, if it exists, consults the trace definition file. The
-%% idea behind the trace definition file is to point out which trace cases there
-%% are, where to find them and how to turn them on and off.
-%% Trace case definitions are:
-%% {TCname,Type,VariableNameList,ActivatioFileName} |
-%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
-%% TCname=atom()
-%% Type=on | on_off
-%% VariableNameList=[atom(),...]
-%% ActivationFileName=DeactivationFileName=string()
-read_trace_case_definitions(LD) ->
- case LD#ld.tc_def_file of
+ {ok,FName};
+ _ -> % Application parameter not specified.
+ false % Means no config file will be used.
+ end.
+
+%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
+read_config_file(FName) ->
+ case catch file:consult(FName) of
+ {ok,Terms} ->
+ {ok,read_config_list(#ld{},Terms)};
+ {error,Reason} ->
+ {error,{file_consult,Reason}};
+ {'EXIT',Reason} ->
+ {error,{failure,Reason}}
+ end.
+
+%% Help function traversing the Terms list entering known tag-values into #ld.
+read_config_list(LD,Terms) ->
+ LD#ld{
+ nodes = case mk_nodes(proplists:get_value(nodes,Terms,LD#ld.nodes)) of
+ {ok,Nodes} -> Nodes;
+ _ -> LD#ld.nodes
+ end,
+ c_node = proplists:get_value(c_node,Terms,LD#ld.c_node), % atom8)
+ regexp_node = proplists:get_value(regexp_node,Terms,LD#ld.regexp_node), % atom()
+ tc_def_file = proplists:get_value(tc_def_file,Terms,LD#ld.tc_def_file),
+ tdg = proplists:get_value(tdg,Terms,LD#ld.tdg),
+ debug = proplists:get_value(debug,Terms,LD#ld.debug),
+ initial_tcs = proplists:get_value(initial_tcs,Terms,LD#ld.initial_tcs),
+ dir = proplists:get_value(dir,Terms,LD#ld.dir),
+ optg = proplists:get_value(optg,Terms,LD#ld.optg)
+ }.
+
+%% -----------------------------------------------------------------------------
+
+
+%% Help function which, if it exists, consults the trace definition file. The
+%% idea behind the trace definition file is to point out which trace cases there
+%% are, where to find them and how to turn them on and off.
+%% Trace case definitions are:
+%% {TCname,Type,VariableNameList,ActivatioFileName} |
+%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
+%% TCname=atom()
+%% Type=on | on_off
+%% VariableNameList=[atom(),...]
+%% ActivationFileName=DeactivationFileName=string()
+read_trace_case_definitions(LD) ->
+ case LD#ld.tc_def_file of
TCfileName when is_list(TCfileName) ->
- case catch file:consult(TCfileName) of
- {ok,Terms} ->
- Dir=LD#ld.dir, % The working directory of the tool.
- TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
- LD#ld{tc_dict=TCdict};
- _ ->
- LD
- end;
- _ ->
- LD
- end.
-
-read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
- FileName=make_absolute_path(FName,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on,
- VarNames,
- FileName,
- TCdict));
-read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
- FileNameOn=make_absolute_path(FNameOn,Dir),
- FileNameOff=make_absolute_path(FNameOff,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on_off,
- VarNames,
- FileNameOn,
- FileNameOff,
- TCdict));
-read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
- read_trace_case_definitions_2(Rest,Dir,TCdict);
-read_trace_case_definitions_2([],_Dir,TCdict) ->
- TCdict.
-
-%% Help function returning an absolute path to FName if FName is not already
-%% absolute. Dir is the working dir of the tool and supposed to be absolute.
-make_absolute_path(FName,Dir) ->
- case filename:pathtype(FName) of
- absolute -> % Then do nothing, allready absolute.
- FName;
- _ ->
- filename:join(Dir,FName)
- end.
-%% -----------------------------------------------------------------------------
-
-get_status(undefined,_Node) ->
- inviso:get_status();
-get_status(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Internal data structure functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% The nodes database structure.
-%% -----------------------------------------------------------------------------
-
-%% The purpose of the nodes database structure is to keep track of what runtime
-%% nodes we have, and their current status.
-%% Implementation:
-%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
-%% non-distributed case.
-%% AvailableStatus={up,Status1} | down
-%% Status1={State,Status} | reactivating
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-%% reactivating=the node is now being brought up to date.
-%% inactive=not tracing, can be initiated and then reactivated.
-%% The following states can occure.
-%% {inactive,running}
-%% Mainly when we start the tool, before a session has been started.
-%% {tracing,running}
-%% When a trace session is on-going.
-%% {trace_failure,running}
-%% If init_tracing failed for some reason.
-%% {tracing,suspended}
-%% reactivating
-%% The node is tracing (has always been) but was suspended. It is now
-%% no longer suspended and the tool is redong commands.
-%% {inactive,suspended}
-%% We can end up here if a session is stopped with this node suspended.
-
-%% Returns a nodes database structure filled with the nodes Nodes.
+ case catch file:consult(TCfileName) of
+ {ok,Terms} ->
+ Dir=LD#ld.dir, % The working directory of the tool.
+ TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
+ LD#ld{tc_dict=TCdict};
+ _ ->
+ LD
+ end;
+ _ ->
+ LD
+ end.
+
+read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
+ FileName=make_absolute_path(FName,Dir),
+ read_trace_case_definitions_2(Rest,
+ Dir,
+ insert_tracecase_tc_dict(TCname,
+ on,
+ VarNames,
+ FileName,
+ TCdict));
+read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
+ FileNameOn=make_absolute_path(FNameOn,Dir),
+ FileNameOff=make_absolute_path(FNameOff,Dir),
+ read_trace_case_definitions_2(Rest,
+ Dir,
+ insert_tracecase_tc_dict(TCname,
+ on_off,
+ VarNames,
+ FileNameOn,
+ FileNameOff,
+ TCdict));
+read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
+ read_trace_case_definitions_2(Rest,Dir,TCdict);
+read_trace_case_definitions_2([],_Dir,TCdict) ->
+ TCdict.
+
+%% Help function returning an absolute path to FName if FName is not already
+%% absolute. Dir is the working dir of the tool and supposed to be absolute.
+make_absolute_path(FName,Dir) ->
+ case filename:pathtype(FName) of
+ absolute -> % Then do nothing, allready absolute.
+ FName;
+ _ ->
+ filename:join(Dir,FName)
+ end.
+%% -----------------------------------------------------------------------------
+
+get_status(undefined,_Node) ->
+ inviso:get_status();
+get_status(CNode,Nodes) ->
+ inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
+%% -----------------------------------------------------------------------------
+
+
+%% =============================================================================
+%% Internal data structure functions.
+%% =============================================================================
+
+%% -----------------------------------------------------------------------------
+%% The nodes database structure.
+%% -----------------------------------------------------------------------------
+
+%% The purpose of the nodes database structure is to keep track of what runtime
+%% nodes we have, and their current status.
+%% Implementation:
+%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
+%% non-distributed case.
+%% AvailableStatus={up,Status1} | down
+%% Status1={State,Status} | reactivating
+%% State=tracing | inactive | trace_failure
+%% Status=running | suspended
+%% reactivating=the node is now being brought up to date.
+%% inactive=not tracing, can be initiated and then reactivated.
+%% The following states can occure.
+%% {inactive,running}
+%% Mainly when we start the tool, before a session has been started.
+%% {tracing,running}
+%% When a trace session is on-going.
+%% {trace_failure,running}
+%% If init_tracing failed for some reason.
+%% {tracing,suspended}
+%% reactivating
+%% The node is tracing (has always been) but was suspended. It is now
+%% no longer suspended and the tool is redong commands.
+%% {inactive,suspended}
+%% We can end up here if a session is stopped with this node suspended.
+
+%% Returns a nodes database structure filled with the nodes Nodes.
mk_nodes(Nodes) when is_list(Nodes) ->
{ok,lists:map(fun(N) when is_atom(N)->{N,down} end,Nodes)};
-mk_nodes(local_runtime) -> % The non-distributed case.
- down;
-mk_nodes(_Nodes) ->
- error.
-%% -----------------------------------------------------------------------------
-
-%% Updates the nodes database structure for each node that has been added.
-%% This is the case when we start the tool or reactivate a node. Note that a node
-%% may have become adopted instead of started.
-%% Returns a new nodes database structure.
-update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added to the control component.
- case get_status(CNode,[Node]) of % Examine if it is tracing or not.
- {ok,[{Node,NodeResult2}]} ->
- Result=mk_nodes_state_from_status(NodeResult2),
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
- {error,_Reason} -> % Strange, mark it as down now.
- update_added_nodes_2(CNode,Node,down,NodesD,Rest)
- end;
- Result ->
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
- end;
-update_added_nodes(_CNode,[],NodesD) ->
- NodesD;
-update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added, most likely autostart.
- mk_nodes_state_from_status(inviso:get_status());
- Result ->
- Result % Simply replace NodesD.
- end.
-
-update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} -> % Node already exists, replace!
- update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
- false -> % Strange, unknown node!
- update_added_nodes(CNode,Rest,NodesD)
- end.
-
-update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
- {up,{tracing,running}};
-update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
- {up,{tracing,suspended}};
-update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
- {up,{inactive,suspended}};
-update_added_nodes_3({ok,new}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,already_added}) ->
- already_added; % This is an error value!
-update_added_nodes_3({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Function marking all nodes that, according to the returnvalue from init_tracing,
-%% now are successfully initiated as tracing and running. Note that nodes that
-%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
-%% Also note that we assume that the nodes must be running to have made it this far.
-%% A node can of course have become suspended in the process, but that node will
-%% be marked as suspended later when that inviso event message arrives to the tool.
-%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
-%% as a result of the init_tracing call (judged from the LogResults).
-set_tracing_running_nodes(undefined,{ok,LogResults},_AvailableStatus) -> % Non-distr. case.
- case set_tracing_running_nodes_checkresult(LogResults) of
- ok ->
- {{up,{tracing,running}},local_runtime};
- error ->
- {down,[]}
- end;
-set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
- {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
-set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
- {down,[]}; % This is questionable!
-set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
- set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
-
-set_tracing_running_nodes_2(CNode,[{Node,{ok,LogResults}}|Rest],NodesD,Nodes) ->
- case set_tracing_running_nodes_checkresult(LogResults) of
- ok -> % The result is good.
- case lists:keysearch(Node,1,NodesD) of
- {value,_} ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
- false -> % Strange.
- set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
- end;
- error -> % This node is not tracing correctly.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
- case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
- {ok,[{Node,NodeResult}]} ->
- Result=mk_nodes_state_from_status(NodeResult),
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
- {error,_Reason} -> % Strange, mark it as down.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
-set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
- {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
-
-%% Help function checking if a returnvalue from inviso:init_tracing really
-%% means that tracing has started as requested.
-set_tracing_running_nodes_checkresult(_LogResults) -> ok. % Should really be better!
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in the NodesD structure and sets it to 'down'.
-%% Returns a new nodes structure.
-set_down_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,down}|Rest];
-set_down_nodes(Node,[NodeStruct|Rest]) ->
- [NodeStruct|set_down_nodes(Node,Rest)];
-set_down_nodes(_,[]) ->
- [];
-set_down_nodes(_,_) -> % Non-distributed case.
- down. % One can argue if this can happend.
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in NodesD to now be suspended. Note that if the node is
-%% reactivating it must be moved to state tracing because that is what is doing.
-set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
- [{Node,{up,{tracing,suspended}}}|Rest];
-set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
- [{Node,{up,{State,suspended}}}|Rest];
-set_suspended_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_suspended_nodes(Node,Rest)];
-set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
- [];
-set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{tracing,suspended}};
-set_suspended_nodes(_,{up,{State,_}}) ->
- {up,{State,suspended}}.
-%% -----------------------------------------------------------------------------
-
-%% This function is called when reactivation is completed. Hence it moves the
-%% node to no longer suspended. Note this can mean that the node is either
-%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
+mk_nodes(local_runtime) -> % The non-distributed case.
+ down;
+mk_nodes(_Nodes) ->
+ error.
+%% -----------------------------------------------------------------------------
+
+%% Updates the nodes database structure for each node that has been added.
+%% This is the case when we start the tool or reactivate a node. Note that a node
+%% may have become adopted instead of started.
+%% Returns a new nodes database structure.
+update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
+ case update_added_nodes_3(NodeResult) of
+ already_added -> % Already added to the control component.
+ case get_status(CNode,[Node]) of % Examine if it is tracing or not.
+ {ok,[{Node,NodeResult2}]} ->
+ Result=mk_nodes_state_from_status(NodeResult2),
+ update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
+ {error,_Reason} -> % Strange, mark it as down now.
+ update_added_nodes_2(CNode,Node,down,NodesD,Rest)
+ end;
+ Result ->
+ update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
+ end;
+update_added_nodes(_CNode,[],NodesD) ->
+ NodesD;
+update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
+ case update_added_nodes_3(NodeResult) of
+ already_added -> % Already added, most likely autostart.
+ mk_nodes_state_from_status(inviso:get_status());
+ Result ->
+ Result % Simply replace NodesD.
+ end.
+
+update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,_} -> % Node already exists, replace!
+ update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
+ false -> % Strange, unknown node!
+ update_added_nodes(CNode,Rest,NodesD)
+ end.
+
+update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
+ {up,{tracing,running}};
+update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
+ {up,{tracing,suspended}};
+update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
+ {up,{inactive,running}};
+update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
+ {up,{inactive,suspended}};
+update_added_nodes_3({ok,new}) ->
+ {up,{inactive,running}};
+update_added_nodes_3({ok,already_added}) ->
+ already_added; % This is an error value!
+update_added_nodes_3({error,_Reason}) ->
+ down.
+%% -----------------------------------------------------------------------------
+
+%% Function marking all nodes that, according to the returnvalue from init_tracing,
+%% now are successfully initiated as tracing and running. Note that nodes that
+%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
+%% Also note that we assume that the nodes must be running to have made it this far.
+%% A node can of course have become suspended in the process, but that node will
+%% be marked as suspended later when that inviso event message arrives to the tool.
+%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
+%% as a result of the init_tracing call (judged from the LogResults).
+set_tracing_running_nodes(undefined,{ok,_LogResults},_AvailableStatus) -> % Non-distr. case.
+ {{up,{tracing,running}},local_runtime};
+set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
+ {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
+set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
+ {down,[]}; % This is questionable!
+set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
+ set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
+
+set_tracing_running_nodes_2(CNode,[{Node,{ok,_LogResults}}|Rest],NodesD,Nodes) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,_} ->
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
+ false -> % Strange.
+ set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
+ end;
+set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
+ case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
+ {ok,[{Node,NodeResult}]} ->
+ Result=mk_nodes_state_from_status(NodeResult),
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
+ {error,_Reason} -> % Strange, mark it as down.
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
+ end;
+set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
+set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
+ {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
+
+%% -----------------------------------------------------------------------------
+
+%% Function updating Node in the NodesD structure and sets it to 'down'.
+%% Returns a new nodes structure.
+set_down_nodes(Node,[{Node,_}|Rest]) ->
+ [{Node,down}|Rest];
+set_down_nodes(Node,[NodeStruct|Rest]) ->
+ [NodeStruct|set_down_nodes(Node,Rest)];
+set_down_nodes(_,[]) ->
+ [];
+set_down_nodes(_,_) -> % Non-distributed case.
+ down. % One can argue if this can happend.
+%% -----------------------------------------------------------------------------
+
+%% Function updating Node in NodesD to now be suspended. Note that if the node is
+%% reactivating it must be moved to state tracing because that is what is doing.
+set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
+ [{Node,{up,{tracing,suspended}}}|Rest];
+set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
+ [{Node,{up,{State,suspended}}}|Rest];
+set_suspended_nodes(Node,[NodesData|Rest]) ->
+ [NodesData|set_suspended_nodes(Node,Rest)];
+set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
+ [];
+set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
+ {up,{tracing,suspended}};
+set_suspended_nodes(_,{up,{State,_}}) ->
+ {up,{State,suspended}}.
+%% -----------------------------------------------------------------------------
+
+%% This function is called when reactivation is completed. Hence it moves the
+%% node to no longer suspended. Note this can mean that the node is either
+%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
set_running_nodes(Node,NodesD) when is_list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
- false -> % Very strange!
- NodesD
- end;
-set_running_nodes(_,NodesD) -> % The non-distributed case.
- set_running_nodes_2(NodesD).
-
-set_running_nodes_2({up,reactivating}) ->
- {up,{tracing,running}};
-set_running_nodes_2({up,{State,suspended}}) ->
- {up,{State,running}}.
-%% -----------------------------------------------------------------------------
-
-%% Function marking node as now reactivating. That means it is not suspended
-%% any longer (and tracing), but still not part of the set of nodes which shall
-%% get all commands. Returns a new NodesD.
-set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,{up,reactivating}}|Rest];
-set_reactivating_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_reactivating_nodes(Node,Rest)];
-set_reactivating_nodes(_,[]) ->
- [];
-set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
- {up,reactivating}.
-%% -----------------------------------------------------------------------------
-
-%% Function called when stop-tracing is done. That is all nodes in Nodes shall
-%% be inactive now. Note that an inactive node can still be suspended.
-%% Returns a new NodesD.
-set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{inactive,running}};
-set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
- {up,{inactive,Status}};
-set_inactive_nodes(_,down) ->
- down;
-set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,{up,reactivating}}} ->
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
- {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
- _ -> % This should not happend.
- set_inactive_nodes(Rest,NodesD)
- end;
-set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
- set_inactive_nodes(Rest,NodesD);
-set_inactive_nodes([],NodesD) ->
- NodesD.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all node names. Note that it can only be used in the
-%% distributed case.
-get_all_nodenames_nodes(NodesD) ->
- lists:map(fun({Node,_})->Node end,NodesD).
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are up, tracing and running (not suspended),
-%% or 'void' in the non-distributed case. This is the list of nodes that shall get
-%% inviso commands.
-get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
- [Node|get_nodenames_running_nodes(Rest)];
-get_nodenames_running_nodes([{_Node,_}|Rest]) ->
- get_nodenames_running_nodes(Rest);
-get_nodenames_running_nodes([]) ->
- [];
-get_nodenames_running_nodes(_) ->
- void. % When non distributed, N/A.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that can be made to initiate tracing.
-get_inactive_running_nodes({up,{inactive,running}}) ->
- local_runtime;
-get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
- [];
-get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
- [Node|get_inactive_running_nodes(Rest)];
-get_inactive_running_nodes([{_Node,_}|Rest]) ->
- get_inactive_running_nodes(Rest);
-get_inactive_running_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that are currently tracing (not necessarily running).
-%% In the non-distributed case the status of the runtime component will be
-%% returned.
-%% Note that nodes showing trace_failure will be included since we like to stop
-%% tracing at those nodes too.
-get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([_|Rest]) ->
- get_tracing_nodes(Rest);
-get_tracing_nodes([]) ->
- [];
-get_tracing_nodes(AvailableStatus) ->
- AvailableStatus.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are currently up.
-get_available_nodes(down) ->
- undefined;
-get_available_nodes([{_Node,down}|Rest]) ->
- get_available_nodes(Rest);
-get_available_nodes([{Node,_}|Rest]) ->
- [Node|get_available_nodes(Rest)];
-get_available_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function returning the "state" of Node. Mainly used to check if the node is
-%% suspended or not.
-%% Returns {State,Status} | reactivating | down
-%% where
+ case lists:keysearch(Node,1,NodesD) of
+ {value,{_,AvailableStatus}} ->
+ lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
+ false -> % Very strange!
+ NodesD
+ end;
+set_running_nodes(_,NodesD) -> % The non-distributed case.
+ set_running_nodes_2(NodesD).
+
+set_running_nodes_2({up,reactivating}) ->
+ {up,{tracing,running}};
+set_running_nodes_2({up,{State,suspended}}) ->
+ {up,{State,running}}.
+%% -----------------------------------------------------------------------------
+
+%% Function marking node as now reactivating. That means it is not suspended
+%% any longer (and tracing), but still not part of the set of nodes which shall
+%% get all commands. Returns a new NodesD.
+set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
+ [{Node,{up,reactivating}}|Rest];
+set_reactivating_nodes(Node,[NodesData|Rest]) ->
+ [NodesData|set_reactivating_nodes(Node,Rest)];
+set_reactivating_nodes(_,[]) ->
+ [];
+set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
+ {up,reactivating}.
+%% -----------------------------------------------------------------------------
+
+%% Function called when stop-tracing is done. That is all nodes in Nodes shall
+%% be inactive now. Note that an inactive node can still be suspended.
+%% Returns a new NodesD.
+set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
+ {up,{inactive,running}};
+set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
+ {up,{inactive,Status}};
+set_inactive_nodes(_,down) ->
+ down;
+set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,{_,{up,reactivating}}} ->
+ set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
+ {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
+ set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
+ _ -> % This should not happend.
+ set_inactive_nodes(Rest,NodesD)
+ end;
+set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
+ set_inactive_nodes(Rest,NodesD);
+set_inactive_nodes([],NodesD) ->
+ NodesD.
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of all node names. Note that it can only be used in the
+%% distributed case.
+get_all_nodenames_nodes(NodesD) ->
+ lists:map(fun({Node,_})->Node end,NodesD).
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of all nodes that are up, tracing and running (not suspended),
+%% or 'void' in the non-distributed case. This is the list of nodes that shall get
+%% inviso commands.
+get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
+ [Node|get_nodenames_running_nodes(Rest)];
+get_nodenames_running_nodes([{_Node,_}|Rest]) ->
+ get_nodenames_running_nodes(Rest);
+get_nodenames_running_nodes([]) ->
+ [];
+get_nodenames_running_nodes(_) ->
+ void. % When non distributed, N/A.
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of nodes that can be made to initiate tracing.
+get_inactive_running_nodes({up,{inactive,running}}) ->
+ local_runtime;
+get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
+ [];
+get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
+ [Node|get_inactive_running_nodes(Rest)];
+get_inactive_running_nodes([{_Node,_}|Rest]) ->
+ get_inactive_running_nodes(Rest);
+get_inactive_running_nodes([]) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of nodes that are currently tracing (not necessarily running).
+%% In the non-distributed case the status of the runtime component will be
+%% returned.
+%% Note that nodes showing trace_failure will be included since we like to stop
+%% tracing at those nodes too.
+get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
+ [Node|get_tracing_nodes(Rest)];
+get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
+ [Node|get_tracing_nodes(Rest)];
+get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
+ [Node|get_tracing_nodes(Rest)];
+get_tracing_nodes([_|Rest]) ->
+ get_tracing_nodes(Rest);
+get_tracing_nodes([]) ->
+ [];
+get_tracing_nodes(AvailableStatus) ->
+ AvailableStatus.
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of all nodes that are currently up.
+get_available_nodes(down) ->
+ undefined;
+get_available_nodes([{_Node,down}|Rest]) ->
+ get_available_nodes(Rest);
+get_available_nodes([{Node,_}|Rest]) ->
+ [Node|get_available_nodes(Rest)];
+get_available_nodes([]) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Function returning the "state" of Node. Mainly used to check if the node is
+%% suspended or not.
+%% Returns {State,Status} | reactivating | down
+%% where
get_state_nodes(Node,NodesD) when is_list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- get_state_nodes_2(AvailableStatus);
- false ->
- false
- end;
-get_state_nodes(_,NodesD) -> % Non distributed case.
- get_state_nodes_2(NodesD).
-
-get_state_nodes_2({up,{trace_failure,Status}}) ->
- {trace_failure,Status};
-get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
- {State,suspended};
-get_state_nodes_2({up,reactivating}) ->
- reactivating;
-get_state_nodes_2({up,{State,running}}) ->
- {State,running};
-get_state_nodes_2(down) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Help function in the case we need to consult the state/status of a runtime
-%% component. Returns a nodesD value that can be added to the nodes database.
-mk_nodes_state_from_status({ok,{tracing,running}}) ->
- {up,{tracing,running}};
-mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
- {up,{tracing,suspended}};
-mk_nodes_state_from_status({ok,{_,running}}) ->
- {up,{inactive,running}};
-mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
- {up,{inactive,suspended}};
-mk_nodes_state_from_status({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The session_state.
-%% -----------------------------------------------------------------------------
-
-%% The session state reflects if the inviso_tool is tracing or not.
-%% This means that if the tool is tracing a reconnected node can be made to
-%% restart_session.
-
-%% Returns the correct value indicating that we are tracing now.
-tracing_sessionstate() ->
- tracing.
-%% -----------------------------------------------------------------------------
-
-%% Returns true or false depending on if we are tracing now or not.
-is_tracing(tracing) ->
- true;
-is_tracing(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Returns the correct value indicating that the tool is not tracing.
-passive_sessionstate() ->
- idle.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The tracer_data datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tracer_data structure collects the tracer data arguments used to init tracing
-%% by this inviso tool. The args are saved per session. Each session has
-%% a number.
-%% Implementation:
-%% Sessions=[{SessionNr,TDGargs},...]
-%% SessionNr=integer()
-%% TDGargs=list(), args given to the tracer data generator
-%% minus the first argument which is the Node name.
-
-%% Function taking tracerdata args structure inserting yet another session.
-%% Returns {SessionNr,NewTDs}.
-insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
- {SNr+1,[{SNr+1,TDGargs}|TDs]};
-insert_td_tracer_data(TDGargs,undefined) ->
- {1,[{1,TDGargs}]}.
-%% -----------------------------------------------------------------------------
-
-%% Returns the latest session nr.
-get_latest_session_nr_tracer_data(undefined) ->
- undefined;
-get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
- SessionNr.
-%% -----------------------------------------------------------------------------
-
-%% Returns the tracer data arguments used when creating the trace data for the
-%% latest session.
-get_latest_tdgargs_tracer_data(undefined) ->
- undefined;
-get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
- TDGargs.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% The tc_dict or trace case dictionary datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tc_dict stores information about all available trace cases.
-%% Implementation:
-%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
-%% TCname=atom()
-%% Type=on | on_off
-%% VarNames=[atom(),...]
-%% FNameOn=FNameOff=string()
-
-%% Returns the empty trace case dictionary.
-mk_tc_dict() ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function inserting a new trace case into the trace case dictionary.
-insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
- [{TCname,on,VarNames,FNameOn}|TCdict].
-insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
- [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
-%% -----------------------------------------------------------------------------
-
-%% Function finding a trace case definition in the tc_dict structure.
-%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
-get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
- {ok,Tuple};
-get_tracecase_tc_dict(TCname,[_|Rest]) ->
- get_tracecase_tc_dict(TCname,Rest);
-get_tracecase_tc_dict(_,[]) ->
- false;
-get_tracecase_tc_dict(_,_) -> % There are no trace cases!
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
-%% function.
-%% Returning {ok,ActivationFileName}.
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
- {ok,FNameOn};
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
- {ok,FNameOn}.
-
-get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
- {ok,FNameOff};
-get_tc_deactivate_fname(_) -> % Not a case with off function.
- false.
-
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
- VarNames;
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
- VarNames.
-
-%% -----------------------------------------------------------------------------
-
-
-%% The Command History Log (CHL) stores commands to make it possible to
-%% reactivate suspended nodes, reconnect restarted nodes, and to make
-%% autostart files.
-%% Each time tracing is initiated (that is started) the CHL is cleared since
-%% it would not make scense to repeat commands from an earlier tracing at
-%% reactivation for instance.
-
-%% Implementation: {NextCounter,OnGoingList,ETStable}
-%% NextCounter=integer(), next command number - to be able to sort them in order.
-%% OnGoingList=[{ProcH,{TCname,ID}},...]
-%% ID=term(), instance id for this execution of this trace case.
-%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
-%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
-%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
-%% {{TCname,#Ref},Counter,Bindings} % An rtc
-%% {{M,F,Args,#Ref},Counter}
-%% Counter=integer(), the order-counter for this logged entry.
-%% State1=activating | stopping
-%% Where:
-%% activating: the activation file for the tracecase is running.
-%% running : activation is completed.
-%% stopping : set on the previously running ETS entry when deactivation
-%% file is currently executing.
-%% stop : entered with own Counter into the ETS table when
-%% deactivation file is executing. Remains after too.
-%% Result=term(), the result returned from the tr-case or inviso call.
-
-
-%% Returning an initial empty CHL.
-mk_chl(undefined) ->
- {1,[],ets:new(inviso_tool_chl,[set,protected])};
-mk_chl({_,_,TId}) ->
- ets:delete(TId),
- mk_chl(undefined).
-
-%% Help function returning 'true' if there is a current history.
-history_exists_chl(undefined) ->
- false;
-history_exists_chl({_,_,_}) ->
- true.
-
-%% Function looking up the state of this trace case.
-find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
- {ok,Bindings};
- [{_,_,State,_}] -> % activating or stopping.
- State;
- [] ->
- false
- end.
-
-%% Function finding the Trace case associated with a process handle
-%% doing this trace case's activation or stopping.
-find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
- case lists:keysearch(ProcH,1,OnGoingList) of
- {value,{_,{TCname,Id}}} ->
- [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
- {State,{TCname,Id}}; % Should be activating or stopping.
- false ->
- false
- end.
-
-%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
-%% is called for trace cases that do not have on/off functionality.
-set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
- ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
- {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function marking a trace case as now running. That is the activation
-%% phase is completed. It is normaly completed when the process executing
-%% the trace case signals that it is done.
-set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
- [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function marking trace case TCname with identifier Id as now in its stopping
-%% state. Where ProcH is the handler to the process running the stopping
-%% trace case.
-set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
- [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
- ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
- {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function removing a TCname-Id from the CHL. This is mostly used
-%% if activating the trace case failed for some reason. We do not then
-%% expect the user to stop the trace case. Hence it must be removed now.
-%% A reactivation process may have noticed the activating-entry and started
-%% to activate it. But since the general state reached after an unsuccessful
-%% activation can not easily be determined, we don't try to do much about it.
-del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function removing the entry TCname+Id from the CHL. This makes it
-%% possible to activate a tracecase with this id again. The entry was
-%% previously marked as stopping.
-nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter+1,NewOnGoingList,TId}.
-
-%% Function stopping all processes saved as being now running tc executers.
-%% This is useful as cleanup during stop tracing for instance.
-%% Returns a new CHL which is not in all parts correct. Entries in the
-%% ETS table are for instance not properly state-changed. But the CHL will
-%% from now on only be used to create command files and similar.
-stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
- exit(ProcH,kill),
- stop_all_tc_executer_chl({NextCounter,Rest,TId});
-stop_all_tc_executer_chl({NextCounter,[],TId}) ->
- {NextCounter,[],TId}.
-
-%% Function adding a "plain" inviso call to the CHL.
-add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
- {NextCounter+1,OnGoingList,TId}.
-
-%% Function adding a run trace case entry to the chl.
-add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
- {NextCounter+1,OnGoingList,TId}.
-%% Returns the highest used counter number in the command history log.
-get_highest_used_counter_chl({NextCounter,_,_}) ->
- NextCounter-1.
-
-%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
-%% assynchronous tracecases.
-get_ongoing_chl(undefined) ->
- [];
-get_ongoing_chl({_,OngoingList,TId}) ->
- get_ongoing_chl_2(OngoingList,TId).
-
-get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_C,activating,_B}] ->
- [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
- [{_,_C,stopping,_B}] ->
- [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
- end;
-get_ongoing_chl_2([],_) ->
- [].
-
-%% Function returning a list of log entries. Note that the list is unsorted
-%% in respect to Counter.
-get_loglist_chl({_,_,TId}) ->
- L=ets:tab2list(TId),
- lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
- (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
- (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
- (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
- (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
- end,
- L);
-get_loglist_chl(_) -> % The history is not initiated, ever!
- [].
-
-%% Function returning a list of log entries, but only those which are not
-%% cancelled out by deactivations.
-% get_loglist_active_chl({_,_,TId}) ->
-% L=ets:tab2list(TId),
-% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
-% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
-% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
-% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
-% end,
-% L);
-% get_loglist_chl(_) -> % The history is not initiated, ever!
-% [].
-
-
-%% This helpfunction recreates a history from a saved history list. This function
-%% is supposed to crash if the log is not well formatted. Note that we must restore
-%% the counter in order for the counter to work if new commands are added to the
-%% history.
-replace_history_chl(OldCHL,SortedLog) ->
- {_,Ongoing,TId}=mk_chl(OldCHL),
- {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
- {ok,{Counter+1,Ongoing,NewTId}}.
-
-replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,Id},C,running,B,undefined}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
- ets:insert(TId,{{M,F,Args,make_ref()},C}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,make_ref()},C,B}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[],Counter) ->
- {TId,Counter}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Reactivators data structure.
-%% -----------------------------------------------------------------------------
-
-%% Function adding a new node-reactivatorpid pair to the reactivators structure.
-%% In this way we know which reactivators to remove if Node terminates, or when
-%% a node is fully updated when a reactivator is done.
-add_reactivators(Node,Pid,Reactivators) ->
- [{Node,Pid}|Reactivators].
-
-%% Function removing a reactivator entry from the reactivators structure.
-del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
- Rest;
-del_reactivators(RPid,[Element|Rest]) ->
- [Element|del_reactivators(RPid,Rest)];
-del_reactivators(_,[]) -> % This should not happend.
- [].
-
-get_node_reactivators(RPid,Reactivators) ->
- case lists:keysearch(RPid,2,Reactivators) of
- {value,{Node,_}} ->
- Node;
- false -> % This should not happend.
- false
- end.
-
-%% Returns a list of list all nodes that are currently reactivating.
-get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
- [Nodes|get_all_nodes_reactivators(Rest)];
-get_all_nodes_reactivators([]) ->
- [].
-
-%% Function stopping all running reactivator processes. Returns a new empty
-%% reactivators structure. Note that this function does not set the state of
-%% Nodes. It must most often be set to running.
-stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
- exit(Pid,kill),
- stop_all_reactivators(Rest);
-stop_all_reactivators([]) ->
- []. % Returns an empty reactivators.
-
-%% Help function stopping the reactivator (if any) that reactivates Node.
-%% Returns a new list of reactivators structure.
-stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
- exit(Pid,kill),
- Rest;
-stop_node_reactivators(Node,[NodePid|Rest]) ->
- [NodePid|stop_node_reactivators(Node,Rest)];
-stop_node_reactivators(_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Started initial trace cases data structure.
-%% -----------------------------------------------------------------------------
-
-%% This datastructure keeps information about ongoing trace cases started
-%% automatically at init_tracing. These must be automatically stopped when calling
-%% stop_tracing.
-
-add_initial_tcs(TCname,Id,StartedInitialTcs) ->
- [{TCname,Id}|StartedInitialTcs].
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ case lists:keysearch(Node,1,NodesD) of
+ {value,{_,AvailableStatus}} ->
+ get_state_nodes_2(AvailableStatus);
+ false ->
+ false
+ end;
+get_state_nodes(_,NodesD) -> % Non distributed case.
+ get_state_nodes_2(NodesD).
+
+get_state_nodes_2({up,{trace_failure,Status}}) ->
+ {trace_failure,Status};
+get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
+ {State,suspended};
+get_state_nodes_2({up,reactivating}) ->
+ reactivating;
+get_state_nodes_2({up,{State,running}}) ->
+ {State,running};
+get_state_nodes_2(down) ->
+ down.
+%% -----------------------------------------------------------------------------
+
+%% Help function in the case we need to consult the state/status of a runtime
+%% component. Returns a nodesD value that can be added to the nodes database.
+mk_nodes_state_from_status({ok,{tracing,running}}) ->
+ {up,{tracing,running}};
+mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
+ {up,{tracing,suspended}};
+mk_nodes_state_from_status({ok,{_,running}}) ->
+ {up,{inactive,running}};
+mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
+ {up,{inactive,suspended}};
+mk_nodes_state_from_status({error,_Reason}) ->
+ down.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% The session_state.
+%% -----------------------------------------------------------------------------
+
+%% The session state reflects if the inviso_tool is tracing or not.
+%% This means that if the tool is tracing a reconnected node can be made to
+%% restart_session.
+
+%% Returns the correct value indicating that we are tracing now.
+tracing_sessionstate() ->
+ tracing.
+%% -----------------------------------------------------------------------------
+
+%% Returns true or false depending on if we are tracing now or not.
+is_tracing(tracing) ->
+ true;
+is_tracing(_) ->
+ false.
+%% -----------------------------------------------------------------------------
+
+%% Returns the correct value indicating that the tool is not tracing.
+passive_sessionstate() ->
+ idle.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% The tracer_data datastructure.
+%% -----------------------------------------------------------------------------
+
+%% The tracer_data structure collects the tracer data arguments used to init tracing
+%% by this inviso tool. The args are saved per session. Each session has
+%% a number.
+%% Implementation:
+%% Sessions=[{SessionNr,TDGargs},...]
+%% SessionNr=integer()
+%% TDGargs=list(), args given to the tracer data generator
+%% minus the first argument which is the Node name.
+
+%% Function taking tracerdata args structure inserting yet another session.
+%% Returns {SessionNr,NewTDs}.
+insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
+ {SNr+1,[{SNr+1,TDGargs}|TDs]};
+insert_td_tracer_data(TDGargs,undefined) ->
+ {1,[{1,TDGargs}]}.
+%% -----------------------------------------------------------------------------
+
+%% Returns the latest session nr.
+get_latest_session_nr_tracer_data(undefined) ->
+ undefined;
+get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
+ SessionNr.
+%% -----------------------------------------------------------------------------
+
+%% Returns the tracer data arguments used when creating the trace data for the
+%% latest session.
+get_latest_tdgargs_tracer_data(undefined) ->
+ undefined;
+get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
+ TDGargs.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% The tc_dict or trace case dictionary datastructure.
+%% -----------------------------------------------------------------------------
+
+%% The tc_dict stores information about all available trace cases.
+%% Implementation:
+%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
+%% TCname=atom()
+%% Type=on | on_off
+%% VarNames=[atom(),...]
+%% FNameOn=FNameOff=string()
+
+%% Returns the empty trace case dictionary.
+mk_tc_dict() ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Function inserting a new trace case into the trace case dictionary.
+insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
+ [{TCname,on,VarNames,FNameOn}|TCdict].
+insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
+ [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
+%% -----------------------------------------------------------------------------
+
+%% Function finding a trace case definition in the tc_dict structure.
+%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
+get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
+ {ok,Tuple};
+get_tracecase_tc_dict(TCname,[_|Rest]) ->
+ get_tracecase_tc_dict(TCname,Rest);
+get_tracecase_tc_dict(_,[]) ->
+ false;
+get_tracecase_tc_dict(_,_) -> % There are no trace cases!
+ false.
+%% -----------------------------------------------------------------------------
+
+%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
+%% function.
+%% Returning {ok,ActivationFileName}.
+get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
+ {ok,FNameOn};
+get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
+ {ok,FNameOn}.
+
+get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
+ {ok,FNameOff};
+get_tc_deactivate_fname(_) -> % Not a case with off function.
+ false.
+
+get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
+ VarNames;
+get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
+ VarNames.
+
+%% -----------------------------------------------------------------------------
+
+
+%% The Command History Log (CHL) stores commands to make it possible to
+%% reactivate suspended nodes, reconnect restarted nodes, and to make
+%% autostart files.
+%% Each time tracing is initiated (that is started) the CHL is cleared since
+%% it would not make scense to repeat commands from an earlier tracing at
+%% reactivation for instance.
+
+%% Implementation: {NextCounter,OnGoingList,ETStable}
+%% NextCounter=integer(), next command number - to be able to sort them in order.
+%% OnGoingList=[{ProcH,{TCname,ID}},...]
+%% ID=term(), instance id for this execution of this trace case.
+%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
+%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
+%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
+%% {{TCname,#Ref},Counter,Bindings} % An rtc
+%% {{M,F,Args,#Ref},Counter}
+%% Counter=integer(), the order-counter for this logged entry.
+%% State1=activating | stopping
+%% Where:
+%% activating: the activation file for the tracecase is running.
+%% running : activation is completed.
+%% stopping : set on the previously running ETS entry when deactivation
+%% file is currently executing.
+%% stop : entered with own Counter into the ETS table when
+%% deactivation file is executing. Remains after too.
+%% Result=term(), the result returned from the tr-case or inviso call.
+
+
+%% Returning an initial empty CHL.
+mk_chl(undefined) ->
+ {1,[],ets:new(inviso_tool_chl,[set,protected])};
+mk_chl({_,_,TId}) ->
+ ets:delete(TId),
+ mk_chl(undefined).
+
+%% Help function returning 'true' if there is a current history.
+history_exists_chl(undefined) ->
+ false;
+history_exists_chl({_,_,_}) ->
+ true.
+
+%% Function looking up the state of this trace case.
+find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
+ case ets:lookup(TId,{TCname,Id}) of
+ [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
+ {ok,Bindings};
+ [{_,_,State,_}] -> % activating or stopping.
+ State;
+ [] ->
+ false
+ end.
+
+%% Function finding the Trace case associated with a process handle
+%% doing this trace case's activation or stopping.
+find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
+ case lists:keysearch(ProcH,1,OnGoingList) of
+ {value,{_,{TCname,Id}}} ->
+ [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
+ {State,{TCname,Id}}; % Should be activating or stopping.
+ false ->
+ false
+ end.
+
+%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
+%% is called for trace cases that do not have on/off functionality.
+set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
+ ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
+ {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
+
+%% Function marking a trace case as now running. That is the activation
+%% phase is completed. It is normaly completed when the process executing
+%% the trace case signals that it is done.
+set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
+ [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
+ ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
+ NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
+ {NextCounter,NewOnGoingList,TId}.
+
+%% Function marking trace case TCname with identifier Id as now in its stopping
+%% state. Where ProcH is the handler to the process running the stopping
+%% trace case.
+set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
+ [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
+ ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
+ ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
+ {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
+
+%% Function removing a TCname-Id from the CHL. This is mostly used
+%% if activating the trace case failed for some reason. We do not then
+%% expect the user to stop the trace case. Hence it must be removed now.
+%% A reactivation process may have noticed the activating-entry and started
+%% to activate it. But since the general state reached after an unsuccessful
+%% activation can not easily be determined, we don't try to do much about it.
+del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
+ ets:delete(TId,{TCname,Id}),
+ NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
+ {NextCounter,NewOnGoingList,TId}.
+
+%% Function removing the entry TCname+Id from the CHL. This makes it
+%% possible to activate a tracecase with this id again. The entry was
+%% previously marked as stopping.
+nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
+ ets:delete(TId,{TCname,Id}),
+ NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
+ {NextCounter+1,NewOnGoingList,TId}.
+
+%% Function stopping all processes saved as being now running tc executers.
+%% This is useful as cleanup during stop tracing for instance.
+%% Returns a new CHL which is not in all parts correct. Entries in the
+%% ETS table are for instance not properly state-changed. But the CHL will
+%% from now on only be used to create command files and similar.
+stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
+ exit(ProcH,kill),
+ stop_all_tc_executer_chl({NextCounter,Rest,TId});
+stop_all_tc_executer_chl({NextCounter,[],TId}) ->
+ {NextCounter,[],TId}.
+
+%% Function adding a "plain" inviso call to the CHL.
+add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
+ ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
+ {NextCounter+1,OnGoingList,TId}.
+
+%% Function adding a run trace case entry to the chl.
+add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
+ ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
+ {NextCounter+1,OnGoingList,TId}.
+%% Returns the highest used counter number in the command history log.
+get_highest_used_counter_chl({NextCounter,_,_}) ->
+ NextCounter-1.
+
+%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
+%% assynchronous tracecases.
+get_ongoing_chl(undefined) ->
+ [];
+get_ongoing_chl({_,OngoingList,TId}) ->
+ get_ongoing_chl_2(OngoingList,TId).
+
+get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
+ case ets:lookup(TId,{TCname,Id}) of
+ [{_,_C,activating,_B}] ->
+ [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
+ [{_,_C,stopping,_B}] ->
+ [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
+ end;
+get_ongoing_chl_2([],_) ->
+ [].
+
+%% Function returning a list of log entries. Note that the list is unsorted
+%% in respect to Counter.
+get_loglist_chl({_,_,TId}) ->
+ L=ets:tab2list(TId),
+ lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
+ (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
+ (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
+ (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
+ (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
+ end,
+ L);
+get_loglist_chl(_) -> % The history is not initiated, ever!
+ [].
+
+%% Function returning a list of log entries, but only those which are not
+%% cancelled out by deactivations.
+% get_loglist_active_chl({_,_,TId}) ->
+% L=ets:tab2list(TId),
+% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
+% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
+% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
+% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
+% end,
+% L);
+% get_loglist_chl(_) -> % The history is not initiated, ever!
+% [].
+
+
+%% This helpfunction recreates a history from a saved history list. This function
+%% is supposed to crash if the log is not well formatted. Note that we must restore
+%% the counter in order for the counter to work if new commands are added to the
+%% history.
+replace_history_chl(OldCHL,SortedLog) ->
+ {_,Ongoing,TId}=mk_chl(OldCHL),
+ {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
+ {ok,{Counter+1,Ongoing,NewTId}}.
+
+replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
+ ets:insert(TId,{{TC,Id},C,running,B,undefined}),
+ replace_history_chl_2(TId,Rest,C);
+replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
+ ets:insert(TId,{{M,F,Args,make_ref()},C}),
+ replace_history_chl_2(TId,Rest,C);
+replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
+ ets:insert(TId,{{TC,make_ref()},C,B}),
+ replace_history_chl_2(TId,Rest,C);
+replace_history_chl_2(TId,[],Counter) ->
+ {TId,Counter}.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% Reactivators data structure.
+%% -----------------------------------------------------------------------------
+
+%% Function adding a new node-reactivatorpid pair to the reactivators structure.
+%% In this way we know which reactivators to remove if Node terminates, or when
+%% a node is fully updated when a reactivator is done.
+add_reactivators(Node,Pid,Reactivators) ->
+ [{Node,Pid}|Reactivators].
+
+%% Function removing a reactivator entry from the reactivators structure.
+del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
+ Rest;
+del_reactivators(RPid,[Element|Rest]) ->
+ [Element|del_reactivators(RPid,Rest)];
+del_reactivators(_,[]) -> % This should not happend.
+ [].
+
+get_node_reactivators(RPid,Reactivators) ->
+ case lists:keysearch(RPid,2,Reactivators) of
+ {value,{Node,_}} ->
+ Node;
+ false -> % This should not happend.
+ false
+ end.
+
+%% Returns a list of list all nodes that are currently reactivating.
+get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
+ [Nodes|get_all_nodes_reactivators(Rest)];
+get_all_nodes_reactivators([]) ->
+ [].
+
+%% Function stopping all running reactivator processes. Returns a new empty
+%% reactivators structure. Note that this function does not set the state of
+%% Nodes. It must most often be set to running.
+stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
+ exit(Pid,kill),
+ stop_all_reactivators(Rest);
+stop_all_reactivators([]) ->
+ []. % Returns an empty reactivators.
+
+%% Help function stopping the reactivator (if any) that reactivates Node.
+%% Returns a new list of reactivators structure.
+stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
+ exit(Pid,kill),
+ Rest;
+stop_node_reactivators(Node,[NodePid|Rest]) ->
+ [NodePid|stop_node_reactivators(Node,Rest)];
+stop_node_reactivators(_,[]) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% Started initial trace cases data structure.
+%% -----------------------------------------------------------------------------
+
+%% This datastructure keeps information about ongoing trace cases started
+%% automatically at init_tracing. These must be automatically stopped when calling
+%% stop_tracing.
+
+add_initial_tcs(TCname,Id,StartedInitialTcs) ->
+ [{TCname,Id}|StartedInitialTcs].
+%% -----------------------------------------------------------------------------
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/inviso/src/inviso_tool_sh.erl b/lib/inviso/src/inviso_tool_sh.erl
index fe876b955a..b02f498c5b 100644
--- a/lib/inviso/src/inviso_tool_sh.erl
+++ b/lib/inviso/src/inviso_tool_sh.erl
@@ -1,1731 +1,1749 @@
-%%%------------------------------------------------------------------------------
-%%% File : inviso_tool_sh.erl
-%%% Author : Lennart �hman <[email protected]>
-%%% Description :
-%%%
-%%% Created : 24 Oct 2005 by Lennart �hman
-%%%------------------------------------------------------------------------------
--module(inviso_tool_sh).
-
-%% Inviso Session Handler.
-%% This is the code for the session handler process. Its purpose is that we have
-%% one session handler process for each trace session started through the
-%% start_session inviso tool API. The session handler process is responsible for:
-%%
-%% -Knowing the state/status of all participating runtime components.
-%% -Keeping storage of all tracerdata all our participants have used. This means
-%% also to find out the tracerdata of runtime components connecting by them
-%% selves.
-%%
-%% STORAGE STRATEGY
-%% ----------------
-%% The local information storage can be changed by two things. Either by executing
-%% commands issued through our APIs. Or by receiving trace_event from the control
-%% component. When we execute commands, a corresponding event will also follow.
-%% Meaning that in those situations we are informed twice.
-%% A simple strategy could be to wait for the event even when doing the changes
-%% to the runtime components our self (through commands). But that may result in
-%% a small time frame where someone might do yet another command and failing
-%% because the local information storage is not uptodate as it would have been
-%% expected to be. Therefore we always update the local storage when making changes
-%% to a runtime component our selves. There will eventually be a double update
-%% through an incoming event. But the storage must coop with that, preventing
-%% inconsitancies to happend. An example of a strategy is that the tracerdata table
-%% is a bag, not allowing for double entries of the same kind. Therefore a double
-%% update is harmless there.
-
-%% ------------------------------------------------------------------------------
-%% Module wide constants.
-%% ------------------------------------------------------------------------------
--define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
--define(TRACING,tracing). % A state defined by the control component.
--define(RUNNING,running). % A status according to control componet.
-
--define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% API exports.
-%% ------------------------------------------------------------------------------
--export([start_link/5,start_link/8]).
--export([cancel_session/1,stop_session/3]).
--export([reactivate/1,reactivate/2]).
--export([tpl/5,tpl/6,tpl/7,
- tf/2,tf/3,
- tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
- tpm_ms/7,ctpm_ms/6,ctpm/5
- ]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Internal exports.
-%% ------------------------------------------------------------------------------
--export([init/1,handle_call/3,handle_info/2,terminate/2]).
-
--export([get_loopdata/1]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Includes.
-%% ------------------------------------------------------------------------------
--include_lib("kernel/include/file.hrl"). % Necessary for file module.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
-%% {ok,Pid} | {error,Reason}
-%% From= pid(), the initial client expecting the reply.
-%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
-%% CtrlNode=atom() | 'void', the node where the trace control component is.
-%% CtrlPid=pid(), the pid of the trace control component.
-%% SafetyCatches=
-%% Dir=string(), where to place fetched logs and the merged log.
-%% Dbg=debug structure.
-%% NodesIn=[Node,...], list of nodes already in another session.
-%% NodesNotIn=[Node,...], list of nodes not in another session.
-%%
-%% Starts a session-handler. It keeps track of the the state and status of all
-%% participating runtime components. Note that there is a non-distributed case too.
-%% In the non-distributed case there is no things such as CtrlNode.
-start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
- gen_server:start_link(?MODULE,
- {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
- []).
-
-start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
- gen_server:start_link(?MODULE,
- {self(),From,NodeParams,CtrlNode,CtrlPid,
- SafetyCatches,Dbg,NodesIn,NodesNotIn},
- []).
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing where it is ongoing. Fetches all logfiles.
-stop_session(SID,Dir,Prefix) ->
- gen_server:call(SID,{stop_session,Dir,Prefix}).
-%% ------------------------------------------------------------------------------
-
-%% stop_session(SID) = ok
-%%
-%% Cancels the session brutaly. All runtime components are made to stop tracing,
-%% all local log files are removed using the tracerdata we know for them.
-cancel_session(SID) ->
- gen_server:call(SID,cancel_session).
-%% ------------------------------------------------------------------------------
-
-%% reactivate(SID) = {ok,
-%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
-%% SID=session id, pid().
-%% Nodes=[Node,...]
-%% NodeResult=[{Node,Result},...]
-%% Result={Good,Bad}
-%% Good,Bad=integer(), the number of redone activities.
-%%
-%% Function which reactivates runtime components being suspended. This is done
-%% replaying all trace flags (in the correct order) to the corresponding nodes.
-%% Note that this may also mean turning flags off. Like first turning them on
-%% then off a split second later.
-reactivate(SID) ->
- gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
-reactivate(SID,Nodes) ->
- gen_server:call(SID,{reactivate,Nodes}).
-%% ------------------------------------------------------------------------------
-
-
-%% tpl(SessionID,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
-%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
-%% ModRegExp=DirRegExp= string()
-%% Func='_' | FunctionName
-%% Arity='_' | integer()
-%% MS=[] | false | a match specification
-%% Opts=[Opts,...]
-%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
-%% Nodes=[NodeName,...]
-tpl(SID,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
-tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
-tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
-tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
-%% See tpl/X for arguments.
-%%
-%% Removes local trace-patterns from functions.
-ctpl(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-tpm_localnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_localnames,Nodes}).
-tpm_globalnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_globalnames,Nodes}).
-
-init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
-init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,
- {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-
-tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
-
-ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
-
-ctpm(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-%% tf(SessionID,Nodes,TraceConfList)=
-%% TraceConfList=[{PidSpec,Flags},...]
-%% PidSpec=pid()|atom()|all|new|existing
-%% Flags=[Flag,...]
-tf(SID,TraceConfList) ->
- gen_server:call(SID,{tf,TraceConfList}).
-tf(SID,Nodes,TraceConfList) ->
- gen_server:call(SID,{tf,Nodes,TraceConfList}).
-%% ------------------------------------------------------------------------------
-
-
-get_loopdata(SID) ->
- gen_server:call(SID,get_loopdata).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Genserver call-backs.
-%% ==============================================================================
-
-%% Initial function for the session handler process. The nodes participating in
-%% the session must previously have been added to our control component by the tool.
-%% The session handler first finds out the state/status of the specified runtime
-%% components, then it tries to initiate tracing on those where it is applicable.
-%% Note that a reply to the initial (tool)client is done from here instead from
-%% the tool-server.
-init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
- {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
- case is_tool_internal_tracerdata(TracerData) of
- false -> % We shall initiate local runtime.
- case inviso:init_tracing(TracerData) of
- ok ->
- gen_server:reply(From,{ok,{self(),ok}}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)};
- {error,Reason} -> % It might have become suspended?!
- gen_server:reply(From,{error,Reason}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)}
- end;
- true -> % We shall not pass this one on.
- gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [],
- [?LOCAL_RUNTIME],
- SafetyCatches,
- Dbg)}
- end;
-init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
- case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
- {ok,States} -> % A list of {Node,{State,Status},Opts}.
- {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
- case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
- {ok,Result} -> % Resulted in state changes!
- RTStates=set_tracing_rtstates(to_rtstates(States),Result),
- ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
- gen_server:reply(From,{ok,{self(),ReplyValue}}),
- {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
- NodeParams2,Nodes2,SafetyCatches,Dbg)};
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,{init_tracing,Reason}}),
- {stop,{init_tracing,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end;
- {error,Reason} -> % Unable to get the state/status.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,Reason}),
- {stop,{error,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end.
-%% ------------------------------------------------------------------------------
-
-%% To stop a session means stop the tracing and remove all local files on the
-%% runtime nodes. We do have a table with all tracer data and that is how we are
-%% going to recreate what files to remove.
-%% Since runtime components may actually change state when this procedure is
-%% on-going, we do not care! It is the state in the session handling process at
-%% the time of start of this procedure which is used.
-handle_call(cancel_session,_From,LD) ->
- CtrlNode=get_ctrlnode_ld(LD),
- RTStates=get_rtstates_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok-> % Hopefully all nodes are stopped now.
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- TRDstorage=get_trdstorage_ld(LD),
- remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
- {stop,normal,ok,LD}; % LD actually not correct now!
- {error,Reason} -> % Some serious error when stop_tracing.
- {stop,normal,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% *Stop all tracing on runtime components still tracing.
-%% *Copy all local log files to the collection directory.
-handle_call({stop_session,Dir,Prefix},_From,LD) ->
- case check_directory_exists(Dir) of % Check that this directory exists here.
- true ->
- RTStates=get_rtstates_ld(LD),
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok -> % Hopefully no node is still tracing now.
- TRDstorage=get_trdstorage_ld(LD),
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {FailedNodes,FetchedFiles}=
- transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
- TRDstorage,Dbg,AvailableNodes),
- RemoveNodes= % We only delete local logs where fetch ok.
- lists:filter(fun(N)->
- case lists:keysearch(N,1,FailedNodes) of
- {value,_} ->
- false;
- false ->
- true
- end
- end,
- AvailableNodes),
- remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
- {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
- {error,Reason} -> % Some general failure, quit.
- {stop,normal,{error,Reason},LD}
- end;
- false -> % You specified a non-existing directory!
- {reply,{error,{faulty_dir,Dir}},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-handle_call({reactivate,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- {OurNodes,OtherNodes}=
- remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
- CtrlNode=get_ctrlnode_ld(LD),
- ACTstorage=get_actstorage_ld(LD),
- case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
- {ok,Results} -> % A list of {Node,Result}.
- if
- OtherNodes==[] -> % Normal case, no non-session nodes.
- {reply,{ok,Results},LD};
- true -> % Add error values for non-session nodes.
- {reply,
- {ok,
- lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
- Results},
- LD}
- end;
- {error,Reason} -> % Then this error takes presidence.
- {reply,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% Call-back for set trace-pattern for both global and local functions.
-handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
- Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
- {reply,Reply,LD};
-handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
- {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
- Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
- {reply,ErrorReply++Reply,LD};
-%% ------------------------------------------------------------------------------
-
-%% Call-back handling the removal of both local and global trace-patterns.
-%% NOT IMPLEMENTED YET.
-handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
- Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
- {reply,Reply,LD};
-%% ------------------------------------------------------------------------------
-
-handle_call({tpm_localnames,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm_ms,
- [Mod,Func,Arity,MSname,MS],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- ctpm_ms,
- [Mod,Func,Arity,MSname],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-%% Call-back for setting process trace-flags. Handles both distributed and non-
-%% distributed case.
-handle_call({tf,TraceConfList},From,LD) ->
- handle_call({tf,all,TraceConfList},From,LD);
-handle_call({tf,Nodes,TraceConfList},_From,LD) ->
- {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
- Nodes,
- TraceConfList,
- get_actstorage_ld(LD),
- get_rtstates_ld(LD)),
- {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-
-
-handle_call(get_loopdata,_From,LD) ->
- io:format("The loopdata:~n~p~n",[LD]),
- {reply,ok,LD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Clause handling an incomming state-change event from the control component.
-%% Note that it does not have to be one of our nodes since it is not possible
-%% to subscribe to certain node-events.
-%% We may very well get state-change events for state-changes we are the source
-%% to our selves. Those state-changes are already incorporated into the RTStates.
-%% There is however no harm in doing them again since we know that this event
-%% message will reach us before a reply to a potentially following state-change
-%% request will reach us. Hence we will do all state-changes in the correct order,
-%% even if sometimes done twice.
-handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case {State,Status} of
- {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
- NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
- Node,
- get_rtstates_ld(LD),
- get_trdstorage_ld(LD)),
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_trdstorage_ld(NewTracerData,
- put_rtstates_ld(NewRTStates,LD))};
- _ -> % In all other cases, just fix rtstates.
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)}
- end;
- _ ->
- {noreply,LD}
- end;
-%% If a new runtime component connects to our trace control component, and it is
-%% in our list of runtime components belonging to this session, we may update its
-%% state to now being present. Otherwise it does not belong to this session.
-%% Note that we avoid updating an already connected runtime component. This
-%% can happend if it connected by itself after we started the session handler,
-%% but before we managed to initiate tracing. Doing so or not will not result in
-%% any error in the long run, but during a short period of time we might be
-%% prevented from doing things with the runtime though it actually is tracing.
-handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
- {ok,unavailable} -> % This is the situation when we update!
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)};
- _ -> % In all other cases, let it be.
- {noreply,LD}
- end;
- _ -> % Not from our control component.
- {noreply,LD}
- end;
-%% If a runtime component disconnects we mark it as unavailable. We must also
-%% remove all saved trace-flags in order for them to not be accidently reactivated
-%% should the runtime component reconnect and then suspend.
-handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
- NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
- {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
- _ ->
- {noreply,LD}
- end;
-handle_info(_,LD) ->
- {noreply,LD}.
-%% ------------------------------------------------------------------------------
-
-%% In terminate we cancel our subscription to event from the trace control component.
-%% That should actually not be necessary, but lets do it the correct way!
-terminate(_,LD) ->
- case get_ctrlnode_ld(LD) of
- void -> % Non-distributed.
- inviso:unsubscribe();
- Node ->
- inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% First level help functions to call-backs.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% Help functions to init.
-%% ------------------------------------------------------------------------------
-
-%% Help function which find out the state/status of the runtime components.
-%% Note that since we have just started subscribe to state changes we must
-%% check our inqueue to see that we have no waiting messages for the nodes
-%% we learned the state/status of. If there is a waiting message we don't
-%% know whether that was a state change received before or after the state
-%% check was done. We will then redo the state-check.
-%% Returns {ok,States} or {error,Reason}.
-%% Where States is [{Node,{State,Status},Opts},...].
-%% Note that {error,Reason} can not occur in the non-distributed case.
-init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
- ok=inviso:subscribe(),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
-init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
- ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
-
-init_rtcomponent_states_2(_,_,_,[],States) ->
- {ok,States};
-init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
- case inviso:get_status() of
- {ok,StateStatus} -> % Got its state/status, now...
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
- [],States),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
- {error,_Reason} -> % The runtime is not available!?
- {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
- end;
-init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
- {ok,NodeResult} ->
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
- {error,Reason} -> % Severe problem, abort the session.
- {error,{get_status,Reason}}
- end.
-
-%% Traverses the list of returnvalues and checks that we do not have an event
-%% waiting in the message queue. If we do have, it is a problem. That node will
-%% be asked about its state again.
-%% Note that it is here we construct the RTStatesList.
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
- receive
- {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
- after
- 0 -> % Not in msg queue, then we're safe!
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,{State,Status},Opts};
- _ -> % No option available, use [].
- {Node,{State,Status},[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
- end;
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,unavailable,Opts};
- _ -> % No option available, use [].
- {Node,unavailable,[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
-init_rtcomponent_states_3(_,_,[],Problems,States) ->
- {Problems,States}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing nodes from NodeParams. The reason for this can either
-%% be that we are using a tool internal tracerdata that shall not be forwarded to
-%% the trace control component, or that the node is actually already part of
-%% another session.
-%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
-remove_nodeparams(Nodes,NodesParams) ->
- remove_nodeparams_2(Nodes,NodesParams,[],[]).
-
-remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
- (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
- Node=element(1,NodeParam),
- Params=element(2,NodeParam), % This is tracerdata!
- case lists:member(Node,Nodes) of
- true -> % Remove this one, in another session.
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
- false -> % Ok so far...
- case is_tool_internal_tracerdata(Params) of
- false -> % Then keep it and use it later!
- remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
- true -> % Since it is, remove it from the list.
- remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
- end
- end;
-remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
-remove_nodeparams_2(_,[],NPAcc,NAcc) ->
- {lists:reverse(NPAcc),NAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds both the nodes which were already part of another
-%% session and the nodes that we actually did not issue any init_tracing for.
-%% Returns a new Result list of [{Node,NodeResult},...].
-init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
- NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
- init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
-
-init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
- [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
-init_fix_resultnodes_2([],_,Result) ->
- Result. % Append Result to the end of the list.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to reactivate.
-%% ------------------------------------------------------------------------------
-
-h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
- {ok,CSuspResults} ->
- {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
- lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
- {[Node|GoodNs],BadNs};
- ({Node,{error,Reason}},{GoodNs,BadNs})->
- {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
- end,
- {[],[]},
- CSuspResults),
- Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
- {ok,BadResults++Results};
- {error,Reason} -> % General failure cancelling suspend.
- {error,{cancel_suspension,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses the list of nodes known to be ours and have
-%% cancelled their suspend. If we fail redoing one of the activities associated
-%% with a node, the node will be reported in the return value as failed. From
-%% that point on its state must be considered unknown since we do not know how
-%% many of the activities were successfully redone.
-h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
- case get_activities_actstorage(Node,ACTstorage) of
- {ok,Activities} -> % The node existed in activity storage.
- {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
- false -> % Node not present in activity storage.
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
- end;
-h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
- lists:reverse(Acc).
-
-%% Help function actually redoing the activity. Note that there must be one
-%% clause here for every type of activity.
-%% Returns {NrGoodCmds,NrBadCmds}.
-%% The number of good or bad commands refers to inviso commands done. If any
-%% of the subparts of such a command returned an error, the command is concidered
-%% no good.
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
- {ok,[{_Node,{ok,Answers}}]} ->
- case h_reactivate_redo_activity_check_tf(Answers) of
- ok ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- error -> % At least oneReports the first encountered error.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
- {ok,[{_Node,ok}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
- {Good,Bad}.
-
-%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
-%% to see if there were any errors.
-h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
- h_reactivate_redo_activity_check_tf(Rest);
-h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
- error;
-h_reactivate_redo_activity_check_tf([]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to tp (setting trace patterns, both local and global).
-%% ------------------------------------------------------------------------------
-
-%% Help function which handles both tpl and tp. Note that the non-distributed case
-%% handled with Nodes='all'.
-%% Returns what shall be the reply to the client.
-h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
- Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
- h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
-h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- SafetyCatches=get_safetycatches_ld(LD),
- case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
- {multinode_expansion,NodeMods} ->
- NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
- {singlenode_expansion,Modules} ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- module ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- wildcard -> % Means do for all modules, no safety.
- h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Note that this function can never be called in the non-distributed case.
-h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
- case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
- {ok,[{Node,Result}]} ->
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
- {error,Reason} -> % Failure, but don't stop.
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
- end;
-h_tp_node_by_node(_,_,_,[],Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function which does the actual call to the trace control component.
-%% Note that Nodes can be a list of nodes (including a single one) or
-%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
-%% detected by the 'void' CtrlNode.
-%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
-%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
-%% {error,Reason}.
-h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
- inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
- case inviso:PatternFunc(TPs) of
- {ok,Result} -> % A list of [Nr1,Nr2,error,...].
- {ok,
- lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
- {AccNr+N,AccErr};
- (error,{AccNr,AccErr}) ->
- {AccNr,AccErr+1}
- end,
- {0,0},
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end;
-h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
- inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
- case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
- {ok,Result} -> % Result is [{Node,Result},...].
- {ok,
- lists:map(fun({Node,{ok,Res}})->
- {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
- {ok,{AccNr+N,AccErr}};
- (error,{AccNr,AccErr}) ->
- {ok,{AccNr,AccErr+1}}
- end,
- {ok,{0,0}},
- Res)};
- ({_Node,{error,Reason}})->
- {error,Reason}
- end,
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions for removing trace-patterns.
-%% ------------------------------------------------------------------------------
-
-%% NOT IMPLEMENTED YET.
-h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
- tbd.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for calling the trace information facility.
-%% ------------------------------------------------------------------------------
-
-
-%% Function handling the meta trace pattern for capturing registration of local
-%% process names.
-h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Functions calling meta trace functions for specified nodes. This function is
-%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
-%% ctpm.
-%% Note that we must store called meta trace functions and their parameters in the
-%% activity storage in order to be able to redo them in case of a reactivate.
-h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for set trace flags.
-%% ------------------------------------------------------------------------------
-
-%% Help function which sets the tracepatterns in TraceConfList for all nodes
-%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
-%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
-%% and NewACTstorage is traceflag storage modified with the flags added to the
-%% corresponding nodes.
-h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
- Reply=inviso:tf(TraceConfList),
- NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
- {Reply,NewACTstorage};
-h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
- AllNodes=get_all_session_nodes_rtstates(RTStates),
- h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
-h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
- {ok,Result} -> % That good we want to modify actstorage!
- NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
- {{ok,Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify actstorage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions to stop_session.
-%% ------------------------------------------------------------------------------
-
-%% This function fetches all local log-files using our stored tracerdata. Note
-%% that there are two major ways of tranfering logfiles. Either via distributed
-%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
-%% But there may be info in the RTStates structure about a common file-system.
-%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
-%% nodenames where problems occurred. Note that problems does not necessarily
-%% mean that no files were copied.
-%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
-%% {ti_log,Files}, listing all files successfully fetched. Note that the
-%% list of fetched files contains sublists of filenames. One for each node and
-%% tracerdata.
-%% In the non-distributed system we always use copy (since the files always
-%% resides locally).
-transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
- if
- CtrlNode==void -> % When non-distributed, always copy!
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
- true -> % The distributed case.
- {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
- {FailedFetchNodes,FetchedFiles}=
- case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
- {ok,Failed,Files} -> % So far no disasters.
- {Failed,Files};
- {error,Reason} -> % Means all fetch-nodes failed!
- inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
- {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
- end,
- {FailedCopyNodes,CopiedFiles}=
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
- {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
- end.
-
-%% Help function which finds out which node we have a common file system with
-%% and from which we must make distributed erlang tranfere.
-%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
-find_logfile_transfer_methods(Nodes,RTStates) ->
- find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
-
-find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
- {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
- case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
- {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
- find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
- {value,_} -> % Can't understand dir option.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
- false -> % Then we want to use fetch instead.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
- end;
-find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
- {FetchAcc,CopyAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transferes all local logfiles according to the tracerdata
-%% stored for the nodes in Nodes.
-%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
-%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
-%% or not at all.
-%% FileNames is a list of list of actually fetched files (the name as it is here, including
-%% Dir). The sublists are files which belong together.
-fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
- LogSpecList=build_logspeclist(Nodes,TRDstorage),
- case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
- fetch_log,
- [LogSpecList,Dir,Prefix])) of
- {ok,Result} ->
- Files=get_all_filenames_fetchlog_result(Result,Dbg),
- FailedNodes=get_all_failednodes_fetchlog_result(Result),
- {ok,FailedNodes,Files};
- {error,Reason} -> % Some general failure!
- {error,{fetch_log,Reason}}
- end.
-
-%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
-%% Note that there may be more than one tracerdata for a node, resulting in multiple
-%% tuples for that node.
-build_logspeclist(Nodes,TRDstorage) ->
- build_logspeclist_2(Nodes,TRDstorage,[]).
-
-build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
- build_logspeclist_2(Rest,
- TRDstorage,
- [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
-build_logspeclist_2([],_,Acc) ->
- lists:flatten(Acc).
-
-%% Help function which translates inviso:fetch_log return values to what I
-%% want!
-inviso_fetch_log({error,Reason}) ->
- {error,Reason};
-inviso_fetch_log({_Success,ResultList}) ->
- {ok,ResultList}.
-
-%% Help function which collects all filenames mentioned in a noderesult structure.
-%% The files may or may not be complete.
-%% Returns a list of list of filenames. Each sublist contains files which belong
-%% together, i.e because they are a wrap-set.
-get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
- get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
-
-get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
- when Success=/=error, list(FileInfo) ->
- SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
-get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
- inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
-get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
- Accum.
-
-get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
- FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
- get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
-get_all_filenames_fetchlog_result_3([],SubAccum) ->
- SubAccum.
-
-%% Help function which traverses a noderesult and builds a list as return
-%% value containing the nodenames of all nodes not being complete.
-%% Note that a node may occur multiple times since may have fetched logfiles
-%% for several tracerdata from the same node. Makes sure the list contains
-%% unique node names.
-%% Returns a list nodes.
-get_all_failednodes_fetchlog_result(NodeResult) ->
- get_all_failednodes_fetchlog_result_2(NodeResult,[]).
-
-get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
-get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
- case lists:member(Node,Acc) of
- true -> % Already in the list.
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
- false -> % Not in Acc, add it!
- get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
- end;
-get_all_failednodes_fetchlog_result_2([],Acc) ->
- Acc.
-%% ------------------------------------------------------------------------------
-
-%% Help function which copies files from one location to Dir and at the same time
-%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
-%% reason the node information is still part of NodeSpecs is that otherwise we can
-%% not report faulty nodes. Note that one node may occur multiple times since there
-%% may be more than one tracerdata for a node.
-%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
-%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
-fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
- CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
-
-fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
- case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
- {0,LocalFiles} -> % Copy went ok and zero errors.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
- {_N,LocalFiles} -> % Copied files, but some went wrong.
- case lists:member(Node,FailedNodes) of
- true -> % Node already in FailedNodes.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
- [{Node,LocalFiles}|Files]);
- false -> % Node not marked as failed, yet.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
- [{Node,LocalFiles}|Files])
- end
- end;
-fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
- {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
-
-fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
- {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
- fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
-fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
- {Errors,Results}.
-
-%% For each file of one file-type (e.g. trace_log).
-fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
- DestName=Prefix++filename:basename(File),
- Destination=filename:join(Dir,DestName),
- case do_copy_file(File,Destination) of
- ok ->
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
- {error,Reason} ->
- inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
- end;
-fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
- {Errors,LocalFiles}.
-
-%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
-%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
-%% is a complete path to a file to be copied.
-build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
- build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
-
-%% For each node specified in the NodeSpecList.
-build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
- CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
- build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
-build_copylist_2(_,_,[],_,Acc) ->
- lists:flatten(Acc).
-
-%% For each tracerdata found for the node.
-build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
- case inviso:list_logs(TRD) of
- {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
- {ok,no_log} -> % This tracedata not associated with any log.
- build_copylist_3(void,Dbg,SourceDir,Node,Rest);
- {error,Reason} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(void,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
- {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
- {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {ok,[{Node,{error,Reason}}]} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(_,_,_,_,[]) ->
- [].
-
-%% Help function which makes a [{Type,Files},...] list where each file in Files
-%% is with full path as found from our file-system.
-build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
- NewFiles=
- lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
- [],
- Files),
- build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
-build_copylist_4(_,[],Accum) ->
- Accum.
-
-
-%% Help function which copies a file using os:cmd.
-%% Returns 'ok' or {error,Reason}.
-do_copy_file(Source,Destination) ->
- case os:type() of
- {win32,_} ->
- os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
- ok;
- {unix,_} ->
- os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
- ok
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function going through the Nodes list and checking that only nodes
-%% mentioned in OurNodes gets returned. It also makes the nodes in the return
-%% value unique.
-remove_nodes_not_ours(Nodes,OurNodes) ->
- remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
-
-remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
- case lists:member(Node,OurNodes) of
- true -> % Ok it is one of our nodes.
- case lists:member(Node,OurAcc) of
- true -> % Already in the list, skip.
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
- end;
- false ->
- case lists:member(Node,OtherAcc) of
- true ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
- end
- end;
-remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
- {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns 'true' or 'false' depending on if TracerData is
-%% meant to be used by the session handler (true) or if it supposed to be passed
-%% on to the trace system.
-is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that all nodes in the first list of nodes exists
-%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
-%% as one incorrect node was found.
-check_our_nodes([Node|Rest],AllNodes) ->
- case lists:member(Node,AllNodes) of
- true ->
- check_our_nodes(Rest,AllNodes);
- false -> % Then we can stop right here.
- false
- end;
-check_our_nodes([],_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that a directory actually exists. Returns 'true' or
-%% 'false'.
-check_directory_exists(Dir) ->
- case file:read_file_info(Dir) of
- {ok,#file_info{type=directory}} ->
- true;
- _ -> % In all other cases it is not valid.
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
-%% of only tracing runtime components. Not that there will actually be any difference
-%% since the return value does not reflect how stopping the nodes went.
-%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
-stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
- case inviso:stop_tracing() of
- {ok,_State} ->
- ok;
- {error,Reason} -> % We actually don't care.
- inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
- ok
- end;
-stop_all_tracing(void,_,_) -> % There is no local runtime started.
- ok;
-stop_all_tracing(CtrlNode,Dbg,Nodes) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
- {ok,Result} -> % The result is only used for debug.
- Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
- (_,Acc)->Acc
- end,
- [],
- Result),
- if
- Failed==[] ->
- ok;
- true ->
- inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
- ok
- end;
- {error,Reason} ->
- {error,{stop_tracing,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all local logs using the tracerdata to determine what
-%% logs to remove from where.
-%% There is no significant return value since it is not really clear what to do
-%% if removal went wrong. The function can make debug-reports thought.
-remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
- LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
- case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
- {ok,Results} ->
- case look_for_errors_resultlist(Results) of
- [] -> % No errors found in the result!
- true;
- Errors ->
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
- true
- end;
- {error,Reason} -> % Some general error.
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
- true
- end.
-
-%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
-%% we must build one tuple for each tracerdata for one node.
-build_logspeclist_remove_logs(Nodes,TRDstorage) ->
- [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses a resultlist from an inviso function. Such are
-%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
-%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
-%% or {ok,FileName}.
-%% Returns a list of {Node,[{error,Reason},...]}.
-look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
- [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
-look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
- case look_for_errors_resultlist_2(NResults,[]) of
- [] ->
- look_for_errors_resultlist(Rest);
- Errors -> % A list of lists.
- [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
- end;
-look_for_errors_resultlist([_|Rest]) ->
- look_for_errors_resultlist(Rest);
-look_for_errors_resultlist([]) ->
- [].
-
-look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
- case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
- [] -> % No errors for this node.
- look_for_errors_resultlist_2(Rest,Accum);
- Errors -> % A list of at least one error.
- look_for_errors_resultlist_2(Rest,[Errors|Accum])
- end;
-look_for_errors_resultlist_2([],Accum) ->
- Accum.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the loopdata structure.
-%% Its main purpose is to store information about runtime components participating
-%% in the session and their current status.
-%% ------------------------------------------------------------------------------
-
--record(ld,{parent,
- ctrlnode,
- ctrlpid, % To where to send inviso cmd.
- rtstates,
- tracerdata,
- safetycatches,
- dbg,
- actstorage % Activity storage, for reactivate.
- }).
-
-%% Function creating the initial datastructure.
-%% The datastructure is [{Node,State},...].
-%%
-%% The tracerdata table is a bag simply for the reason that if we try to insert
-%% the same tracerdata for a node twice, we will end up with one tracerdata after
-%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
-%% come as a state-change too.
-mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
- TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
- TRDtid=ets:new(TRDtableName,[bag]),
- ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
- ACTtid=ets:new(ACTtableName,[bag]),
- mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
- #ld{parent=Parent, % The tool main process.
- ctrlnode=CtrlNode, % Node name where the control component is.
- ctrlpid=CtrlPid, % The process id of the control component.
- rtstates=RTStates, % All nodes and their state/status.
- tracerdata=TRDtid,
- safetycatches=SafetyCatches,
- dbg=Dbg,
- actstorage=ACTtid
- }.
-
-%% Help function which inserts tracer data for the nodes. Note that we can get
-%% tracer data either from the return value from init_tracing or by asking the
-%% node for it. The latter is necessary for the nodes which were marked not to
-%% be initiated by the session handler. This maybe because those nodes have
-%% autostarted.
-mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
- mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
- mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
-
-mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_nodeparams(TId,Rest);
-mk_ld_fill_tracerdata_nodeparams(_,[]) ->
- ok.
-
-mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
- ok;
-mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
- case inviso:get_tracerdata() of
- {error,_Reason} -> % Perhaps in state new or disconnected.
- ok; % Do nothing.
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData})
- end;
-mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
- case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
- {ok,Results} ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Results);
- {error,_Reason} -> % Strange, we will probably crash later.
- ok
- end.
-
-mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
-mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
-mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
- CtrlNode.
-%% ------------------------------------------------------------------------------
-
-
-get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
- CtrlPid.
-%% ------------------------------------------------------------------------------
-
-get_rtstates_ld(#ld{rtstates=RTStates}) ->
- RTStates.
-
-put_rtstates_ld(NewRTStates,LD) ->
- LD#ld{rtstates=NewRTStates}.
-%% ------------------------------------------------------------------------------
-
-get_trdstorage_ld(#ld{tracerdata=TId}) ->
- TId.
-
-put_trdstorage_ld(_NewTId,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds the current tracerdata of node Node to the tracerdata
-%% storage. We only want to add tracerdata we have not seen before. We therefore
-%% avoid adding it if the node already is in state ?TRACING.
-%% Returns a new tracerdata (what ever it is)!
-add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
- case get_statestatus_rtstates(Node,RTStates) of
- {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
- TId; % Then do nothing.
- {ok,_} -> % Since we were not tracing before.
- case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData});
- no_tracerdata -> % Strange, how could we become tracing
- ok;
- {error,_Reason} -> % The node perhaps disconnected!?
- ok
- end;
- false -> % Very strange, not our node!
- ok % Do nothing.
- end.
-
-add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
- case inviso:get_tracerdata() of
- {ok,TracerData} ->
- {ok,TracerData};
- {error,no_tracerdata} ->
- no_tracerdata;
- {error,Reason} ->
- {error,Reason}
- end;
-add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
- {ok,[{Node,{ok,TracerData}}]} ->
- {ok,TracerData};
- {ok,[{Node,{error,no_tracerdata}}]} ->
- no_tracerdata;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-get_safetycatches_ld(#ld{safetycatches=SCs}) ->
- SCs.
-%% ------------------------------------------------------------------------------
-
-get_dbg_ld(#ld{dbg=Dbg}) ->
- Dbg.
-%% ------------------------------------------------------------------------------
-
-get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
- ACTstorage.
-
-put_actstorage_ld(_NewACTstorage,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the rtstates structure (which is a substructure of loopdata).
-%% It is either:
-%% [{Node,StateStatus,Opts},...]
-%% Node is either the node name of the runtime component erlang node or
-%% ?LOCAL_RUNTIME as returned from the trace control component.
-%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
-%% Status is the returnvalue from trace control component.
-%% i.e: running | {suspended,Reason}
-%% ------------------------------------------------------------------------------
-
-%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
-to_rtstates(ListOfStates) when list(ListOfStates) ->
- ListOfStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
-from_rtstates(RTStates) ->
- RTStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes an rtstates structure and a result as returned from
-%% init_tracing. The RTStates is modified for the nodes that changed state as a
-%% result of successful init_tracing.
-%% Returns a new RTStates.
-set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
- case lists:keysearch(Node,1,Result) of
- {value,{_,ok}} -> % Means state-change to tracing!
- [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
- _ -> % Otherwise, leave it as is.
- [E|set_tracing_rtstates(Rest,Result)]
- end;
-set_tracing_rtstates([],_Result) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component.
-%% Returns a new RTStates structure. Note that Node must not necessarily be one
-%% of the nodes in the session. Meaning that Node shall not be added to RTStates
-%% should it not already be in there.
-statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component. The
-%% state/status is set to 'unavailable'.
-%% Returns a new RTStates structure.
-set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function finding the statestatus associated with Node in the RTStates structure.
-%% Returns {ok,StateStatus} or 'false'.
-get_statestatus_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,StateStatus,_}} ->
- {ok,StateStatus};
- false ->
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns a list of all nodes that are currently marked
-%% as available to us in the runtime state structure.
-get_all_available_nodes_rtstates(RTStates) ->
- get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
- (_)->true
- end,
- RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Help function returning a list of all nodes belonging to this session.
-get_all_session_nodes_rtstates(RTStates) ->
- lists:map(fun({Node,_,_})->Node end,RTStates).
-%% ------------------------------------------------------------------------------
-
-%% Function which returns a list of nodes that are indicated as tracing in the
-%% RTStates structure.
-get_all_tracing_nodes_rtstates(RTStates) ->
- lists:map(fun({N,_,_})->N end,
- lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Returns the options associated with Node in the RTStates structure.
-get_opts_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- {ok,Opts};
- false ->
- false
- end.
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the tracerdata structure, which is a part of the loopdata.
-%% The tracerdata structure is an ETS-table of type bag storing:
-%% {Node,TracerData}.
-%% Note that there can of course be multiple entries for a node.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a tracerdata loopdata structure and returns a list
-%% of all stored tracerdata for a certain Node.
-find_tracerdata_for_node_trd(Node,TRD) ->
- case ets:lookup(TRD,Node) of
- Result when list(Result) ->
- lists:map(fun({_Node,TracerData})->TracerData end,Result);
- _ -> % Should probably never happend.
- []
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the activity storage structure, which is part of the
-%% loopdata. It stores entries about things that needs to be "redone" in case
-%% of a reactivation of the node. The time order is also important.
-%% Note that for every ActivityType there must be a "handler" in the reactivation
-%% functionality.
-%%
-%% The structure is a bag of {Node,ActivityType,What}.
-%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
-%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
-%% /{Op,[]}
-%% TraceConfList=[{Proc,Flags},...]
-%% How=true|false
-%% ------------------------------------------------------------------------------
-
-%% Function that adds meta-pattern activities to the activity storage. Note
-%% that one of the parameters to the function is a return value from an
-%% inviso call. In that way we do not enter activities that were unsuccessful.
-%% Op can be either the setting or clearing of a meta pattern.
-%% Returns a new ACTstorage.
-add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-
-%% Function that adds process trace-flags to the activity storage. Note that one
-%% of the parameters is the return value from an inviso function. Meaning that
-%% if the flags failed in their entirety, no activity will be saved. If only
-%% some of the flags failed, we will not go through the effort of trying to find
-%% out exactly which.
-%% Returns a new activity storage structure.
-add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
-%% Finds all activities associated with Node. Returns a list of them in the
-%% same order as they were inserted.
-get_activities_actstorage(Node,ACTstorage) ->
- case ets:lookup(ACTstorage,Node) of
- [] ->
- false;
- Result when list(Result) ->
- {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function removing all activity entries associated with Node. This is useful
-%% if the Node disconnects for instance.
-del_node_actstorage(Node,ACTstorage) ->
- ets:delete(ACTstorage,Node),
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Description:
+%% The runtime component of the trace tool Inviso.
+%%
+%% Authors:
+%% Lennart �hman, [email protected]
+%% -----------------------------------------------------------------------------
+
+-module(inviso_tool_sh).
+
+%% Inviso Session Handler.
+%% This is the code for the session handler process. Its purpose is that we have
+%% one session handler process for each trace session started through the
+%% start_session inviso tool API. The session handler process is responsible for:
+%%
+%% -Knowing the state/status of all participating runtime components.
+%% -Keeping storage of all tracerdata all our participants have used. This means
+%% also to find out the tracerdata of runtime components connecting by them
+%% selves.
+%%
+%% STORAGE STRATEGY
+%% ----------------
+%% The local information storage can be changed by two things. Either by executing
+%% commands issued through our APIs. Or by receiving trace_event from the control
+%% component. When we execute commands, a corresponding event will also follow.
+%% Meaning that in those situations we are informed twice.
+%% A simple strategy could be to wait for the event even when doing the changes
+%% to the runtime components our self (through commands). But that may result in
+%% a small time frame where someone might do yet another command and failing
+%% because the local information storage is not uptodate as it would have been
+%% expected to be. Therefore we always update the local storage when making changes
+%% to a runtime component our selves. There will eventually be a double update
+%% through an incoming event. But the storage must coop with that, preventing
+%% inconsitancies to happend. An example of a strategy is that the tracerdata table
+%% is a bag, not allowing for double entries of the same kind. Therefore a double
+%% update is harmless there.
+
+%% ------------------------------------------------------------------------------
+%% Module wide constants.
+%% ------------------------------------------------------------------------------
+-define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
+-define(TRACING,tracing). % A state defined by the control component.
+-define(RUNNING,running). % A status according to control componet.
+
+-define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% API exports.
+%% ------------------------------------------------------------------------------
+-export([start_link/5,start_link/8]).
+-export([cancel_session/1,stop_session/3]).
+-export([reactivate/1,reactivate/2]).
+-export([ctpl/5,tpl/5,tpl/6,tpl/7,
+ tf/2,tf/3,
+ tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
+ tpm_ms/7,ctpm_ms/6,ctpm/5
+ ]).
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Internal exports.
+%% ------------------------------------------------------------------------------
+-export([init/1,handle_call/3,handle_info/2,terminate/2]).
+
+-export([get_loopdata/1]).
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Includes.
+%% ------------------------------------------------------------------------------
+-include_lib("kernel/include/file.hrl"). % Necessary for file module.
+%% ------------------------------------------------------------------------------
+
+
+%% ==============================================================================
+%% Exported API functions.
+%% ==============================================================================
+
+%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
+%% {ok,Pid} | {error,Reason}
+%% From= pid(), the initial client expecting the reply.
+%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
+%% CtrlNode=atom() | 'void', the node where the trace control component is.
+%% CtrlPid=pid(), the pid of the trace control component.
+%% SafetyCatches=
+%% Dir=string(), where to place fetched logs and the merged log.
+%% Dbg=debug structure.
+%% NodesIn=[Node,...], list of nodes already in another session.
+%% NodesNotIn=[Node,...], list of nodes not in another session.
+%%
+%% Starts a session-handler. It keeps track of the the state and status of all
+%% participating runtime components. Note that there is a non-distributed case too.
+%% In the non-distributed case there is no things such as CtrlNode.
+start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
+ gen_server:start_link(?MODULE,
+ {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
+ []).
+
+start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
+ gen_server:start_link(?MODULE,
+ {self(),From,NodeParams,CtrlNode,CtrlPid,
+ SafetyCatches,Dbg,NodesIn,NodesNotIn},
+ []).
+%% ------------------------------------------------------------------------------
+
+%% Stops tracing where it is ongoing. Fetches all logfiles.
+stop_session(SID,Dir,Prefix) ->
+ gen_server:call(SID,{stop_session,Dir,Prefix}).
+%% ------------------------------------------------------------------------------
+
+%% stop_session(SID) = ok
+%%
+%% Cancels the session brutaly. All runtime components are made to stop tracing,
+%% all local log files are removed using the tracerdata we know for them.
+cancel_session(SID) ->
+ gen_server:call(SID,cancel_session).
+%% ------------------------------------------------------------------------------
+
+%% reactivate(SID) = {ok,
+%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
+%% SID=session id, pid().
+%% Nodes=[Node,...]
+%% NodeResult=[{Node,Result},...]
+%% Result={Good,Bad}
+%% Good,Bad=integer(), the number of redone activities.
+%%
+%% Function which reactivates runtime components being suspended. This is done
+%% replaying all trace flags (in the correct order) to the corresponding nodes.
+%% Note that this may also mean turning flags off. Like first turning them on
+%% then off a split second later.
+reactivate(SID) ->
+ gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
+reactivate(SID,Nodes) ->
+ gen_server:call(SID,{reactivate,Nodes}).
+%% ------------------------------------------------------------------------------
+
+
+%% tpl(SessionID,Mod,Func,Arity,MS)=
+%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
+%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
+%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
+%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
+%% ModRegExp=DirRegExp= string()
+%% Func='_' | FunctionName
+%% Arity='_' | integer()
+%% MS=[] | false | a match specification
+%% Opts=[Opts,...]
+%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
+%% Nodes=[NodeName,...]
+tpl(SID,Mod,Func,Arity,MS) ->
+ gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
+tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
+ gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
+tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
+ gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
+tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
+ gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
+%% ------------------------------------------------------------------------------
+
+%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
+%% See tpl/X for arguments.
+%%
+%% Removes local trace-patterns from functions.
+ctpl(SID,Nodes,Mod,Func,Arity) ->
+ gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
+%% ------------------------------------------------------------------------------
+
+
+tpm_localnames(SID,Nodes) ->
+ gen_server:call(SID,{tpm_localnames,Nodes}).
+
+%% tpm_globalnames(SID,Nodes) ->
+%% gen_server:call(SID,{tpm_globalnames,Nodes}).
+
+init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
+ gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
+init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
+ gen_server:call(SID,
+ {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
+tpm(SID,Nodes,Mod,Func,Arity,MS) ->
+ gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
+tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
+ gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
+tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
+ gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
+
+tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
+ gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
+
+ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
+ gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
+
+ctpm(SID,Nodes,Mod,Func,Arity) ->
+ gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
+%% ------------------------------------------------------------------------------
+
+
+%% tf(SessionID,Nodes,TraceConfList)=
+%% TraceConfList=[{PidSpec,Flags},...]
+%% PidSpec=pid()|atom()|all|new|existing
+%% Flags=[Flag,...]
+tf(SID,TraceConfList) ->
+ gen_server:call(SID,{tf,TraceConfList}).
+tf(SID,Nodes,TraceConfList) ->
+ gen_server:call(SID,{tf,Nodes,TraceConfList}).
+%% ------------------------------------------------------------------------------
+
+
+get_loopdata(SID) ->
+ gen_server:call(SID,get_loopdata).
+%% ------------------------------------------------------------------------------
+
+%% ==============================================================================
+%% Genserver call-backs.
+%% ==============================================================================
+
+%% Initial function for the session handler process. The nodes participating in
+%% the session must previously have been added to our control component by the tool.
+%% The session handler first finds out the state/status of the specified runtime
+%% components, then it tries to initiate tracing on those where it is applicable.
+%% Note that a reply to the initial (tool)client is done from here instead from
+%% the tool-server.
+init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
+ {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
+ case is_tool_internal_tracerdata(TracerData) of
+ false -> % We shall initiate local runtime.
+ case inviso:init_tracing(TracerData) of
+ ok ->
+ gen_server:reply(From,{ok,{self(),ok}}),
+ {ok,mk_ld(Parent,
+ void,
+ CtrlPid,
+ to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
+ [{?LOCAL_RUNTIME,TracerData}],
+ [],
+ SafetyCatches,
+ Dbg)};
+ {error,Reason} -> % It might have become suspended?!
+ gen_server:reply(From,{error,Reason}),
+ {ok,mk_ld(Parent,
+ void,
+ CtrlPid,
+ to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
+ [{?LOCAL_RUNTIME,TracerData}],
+ [],
+ SafetyCatches,
+ Dbg)}
+ end;
+ true -> % We shall not pass this one on.
+ gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
+ {ok,mk_ld(Parent,
+ void,
+ CtrlPid,
+ to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
+ [],
+ [?LOCAL_RUNTIME],
+ SafetyCatches,
+ Dbg)}
+ end;
+init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
+ case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
+ {ok,States} -> % A list of {Node,{State,Status},Opts}.
+ {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
+ {ok,Result} -> % Resulted in state changes!
+ RTStates=set_tracing_rtstates(to_rtstates(States),Result),
+ ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
+ gen_server:reply(From,{ok,{self(),ReplyValue}}),
+ {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
+ NodeParams2,Nodes2,SafetyCatches,Dbg)};
+ {error,Reason} -> % Some general failure.
+ inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
+ gen_server:reply(From,{error,{init_tracing,Reason}}),
+ {stop,{init_tracing,Reason}};
+ What ->
+ io:format("GOT:~n~w~n",[What]),
+ exit(foo)
+ end;
+ {error,Reason} -> % Unable to get the state/status.
+ inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
+ gen_server:reply(From,{error,Reason}),
+ {stop,{error,Reason}};
+ What ->
+ io:format("GOT:~n~w~n",[What]),
+ exit(foo)
+ end.
+%% ------------------------------------------------------------------------------
+
+%% To stop a session means stop the tracing and remove all local files on the
+%% runtime nodes. We do have a table with all tracer data and that is how we are
+%% going to recreate what files to remove.
+%% Since runtime components may actually change state when this procedure is
+%% on-going, we do not care! It is the state in the session handling process at
+%% the time of start of this procedure which is used.
+handle_call(cancel_session,_From,LD) ->
+ CtrlNode=get_ctrlnode_ld(LD),
+ RTStates=get_rtstates_ld(LD),
+ Dbg=get_dbg_ld(LD),
+ TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
+ case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
+ ok-> % Hopefully all nodes are stopped now.
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ TRDstorage=get_trdstorage_ld(LD),
+ remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
+ {stop,normal,ok,LD}; % LD actually not correct now!
+ {error,Reason} -> % Some serious error when stop_tracing.
+ {stop,normal,{error,Reason},LD}
+ end;
+%% ------------------------------------------------------------------------------
+
+%% *Stop all tracing on runtime components still tracing.
+%% *Copy all local log files to the collection directory.
+handle_call({stop_session,Dir,Prefix},_From,LD) ->
+ case check_directory_exists(Dir) of % Check that this directory exists here.
+ true ->
+ RTStates=get_rtstates_ld(LD),
+ CtrlNode=get_ctrlnode_ld(LD),
+ Dbg=get_dbg_ld(LD),
+ TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
+ case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
+ ok -> % Hopefully no node is still tracing now.
+ TRDstorage=get_trdstorage_ld(LD),
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ {FailedNodes,FetchedFiles}=
+ transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
+ TRDstorage,Dbg,AvailableNodes),
+ RemoveNodes= % We only delete local logs where fetch ok.
+ lists:filter(fun(N)->
+ case lists:keysearch(N,1,FailedNodes) of
+ {value,_} ->
+ false;
+ false ->
+ true
+ end
+ end,
+ AvailableNodes),
+ remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
+ {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
+ {error,Reason} -> % Some general failure, quit.
+ {stop,normal,{error,Reason},LD}
+ end;
+ false -> % You specified a non-existing directory!
+ {reply,{error,{faulty_dir,Dir}},LD}
+ end;
+%% ------------------------------------------------------------------------------
+
+handle_call({reactivate,Nodes},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ {OurNodes,OtherNodes}=
+ remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
+ CtrlNode=get_ctrlnode_ld(LD),
+ ACTstorage=get_actstorage_ld(LD),
+ case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
+ {ok,Results} -> % A list of {Node,Result}.
+ if
+ OtherNodes==[] -> % Normal case, no non-session nodes.
+ {reply,{ok,Results},LD};
+ true -> % Add error values for non-session nodes.
+ {reply,
+ {ok,
+ lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
+ Results},
+ LD}
+ end;
+ {error,Reason} -> % Then this error takes presidence.
+ {reply,{error,Reason},LD}
+ end;
+%% ------------------------------------------------------------------------------
+
+%% Call-back for set trace-pattern for both global and local functions.
+handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
+ Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
+ {reply,Reply,LD};
+handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
+ {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
+ Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
+ {reply,ErrorReply++Reply,LD};
+%% ------------------------------------------------------------------------------
+
+%% Call-back handling the removal of both local and global trace-patterns.
+%% NOT IMPLEMENTED YET.
+handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
+ Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
+ {reply,Reply,LD};
+%% ------------------------------------------------------------------------------
+
+handle_call({tpm_localnames,Nodes},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ init_tpm,
+ [Mod,Func,Arity,CallFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ init_tpm,
+ [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ tpm,
+ [Mod,Func,Arity,MS,CallFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ tpm,
+ [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ tpm_ms,
+ [Mod,Func,Arity,MSname,MS],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ ctpm_ms,
+ [Mod,Func,Arity,MSname],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+%% ------------------------------------------------------------------------------
+
+%% Call-back for setting process trace-flags. Handles both distributed and non-
+%% distributed case.
+handle_call({tf,TraceConfList},From,LD) ->
+ handle_call({tf,all,TraceConfList},From,LD);
+handle_call({tf,Nodes,TraceConfList},_From,LD) ->
+ {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
+ Nodes,
+ TraceConfList,
+ get_actstorage_ld(LD),
+ get_rtstates_ld(LD)),
+ {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
+%% ------------------------------------------------------------------------------
+
+
+
+handle_call(get_loopdata,_From,LD) ->
+ io:format("The loopdata:~n~p~n",[LD]),
+ {reply,ok,LD}.
+%% ------------------------------------------------------------------------------
+
+
+%% Clause handling an incomming state-change event from the control component.
+%% Note that it does not have to be one of our nodes since it is not possible
+%% to subscribe to certain node-events.
+%% We may very well get state-change events for state-changes we are the source
+%% to our selves. Those state-changes are already incorporated into the RTStates.
+%% There is however no harm in doing them again since we know that this event
+%% message will reach us before a reply to a potentially following state-change
+%% request will reach us. Hence we will do all state-changes in the correct order,
+%% even if sometimes done twice.
+handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
+ case get_ctrlpid_ld(LD) of
+ CtrlPid -> % It is from our control component.
+ case {State,Status} of
+ {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
+ NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
+ Node,
+ get_rtstates_ld(LD),
+ get_trdstorage_ld(LD)),
+ NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
+ {noreply,put_trdstorage_ld(NewTracerData,
+ put_rtstates_ld(NewRTStates,LD))};
+ _ -> % In all other cases, just fix rtstates.
+ NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
+ {noreply,put_rtstates_ld(NewRTStates,LD)}
+ end;
+ _ ->
+ {noreply,LD}
+ end;
+%% If a new runtime component connects to our trace control component, and it is
+%% in our list of runtime components belonging to this session, we may update its
+%% state to now being present. Otherwise it does not belong to this session.
+%% Note that we avoid updating an already connected runtime component. This
+%% can happend if it connected by itself after we started the session handler,
+%% but before we managed to initiate tracing. Doing so or not will not result in
+%% any error in the long run, but during a short period of time we might be
+%% prevented from doing things with the runtime though it actually is tracing.
+handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
+ case get_ctrlpid_ld(LD) of
+ CtrlPid -> % It is from our control component.
+ case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
+ {ok,unavailable} -> % This is the situation when we update!
+ NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
+ {noreply,put_rtstates_ld(NewRTStates,LD)};
+ _ -> % In all other cases, let it be.
+ {noreply,LD}
+ end;
+ _ -> % Not from our control component.
+ {noreply,LD}
+ end;
+%% If a runtime component disconnects we mark it as unavailable. We must also
+%% remove all saved trace-flags in order for them to not be accidently reactivated
+%% should the runtime component reconnect and then suspend.
+handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
+ case get_ctrlpid_ld(LD) of
+ CtrlPid -> % It is from our control component.
+ NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
+ NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
+ {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
+ _ ->
+ {noreply,LD}
+ end;
+handle_info(_,LD) ->
+ {noreply,LD}.
+%% ------------------------------------------------------------------------------
+
+%% In terminate we cancel our subscription to event from the trace control component.
+%% That should actually not be necessary, but lets do it the correct way!
+terminate(_,LD) ->
+ case get_ctrlnode_ld(LD) of
+ void -> % Non-distributed.
+ inviso:unsubscribe();
+ Node ->
+ inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
+ end.
+%% ------------------------------------------------------------------------------
+
+
+
+%% ==============================================================================
+%% First level help functions to call-backs.
+%% ==============================================================================
+
+%% ------------------------------------------------------------------------------
+%% Help functions to init.
+%% ------------------------------------------------------------------------------
+
+%% Help function which find out the state/status of the runtime components.
+%% Note that since we have just started subscribe to state changes we must
+%% check our inqueue to see that we have no waiting messages for the nodes
+%% we learned the state/status of. If there is a waiting message we don't
+%% know whether that was a state change received before or after the state
+%% check was done. We will then redo the state-check.
+%% Returns {ok,States} or {error,Reason}.
+%% Where States is [{Node,{State,Status},Opts},...].
+%% Note that {error,Reason} can not occur in the non-distributed case.
+init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
+ ok=inviso:subscribe(),
+ init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
+init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
+ ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
+ init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
+
+init_rtcomponent_states_2(_,_,_,[],States) ->
+ {ok,States};
+init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
+ case inviso:get_status() of
+ {ok,StateStatus} -> % Got its state/status, now...
+ {ProblemNodes,NewStates}=
+ init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
+ [],States),
+ init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
+ {error,_Reason} -> % The runtime is not available!?
+ {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
+ end;
+init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
+ {ok,NodeResult} ->
+ {ProblemNodes,NewStates}=
+ init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
+ init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
+ {error,Reason} -> % Severe problem, abort the session.
+ {error,{get_status,Reason}}
+ end.
+
+%% Traverses the list of returnvalues and checks that we do not have an event
+%% waiting in the message queue. If we do have, it is a problem. That node will
+%% be asked about its state again.
+%% Note that it is here we construct the RTStatesList.
+init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
+ receive
+ {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
+ init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
+ after
+ 0 -> % Not in msg queue, then we're safe!
+ RTState=case lists:keysearch(Node,1,NodeParams) of
+ {value,{_Node,_TracerData,Opts}} ->
+ {Node,{State,Status},Opts};
+ _ -> % No option available, use [].
+ {Node,{State,Status},[]}
+ end,
+ init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
+ end;
+init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
+ RTState=case lists:keysearch(Node,1,NodeParams) of
+ {value,{_Node,_TracerData,Opts}} ->
+ {Node,unavailable,Opts};
+ _ -> % No option available, use [].
+ {Node,unavailable,[]}
+ end,
+ init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
+init_rtcomponent_states_3(_,_,[],Problems,States) ->
+ {Problems,States}.
+%% ------------------------------------------------------------------------------
+
+%% Help function removing nodes from NodeParams. The reason for this can either
+%% be that we are using a tool internal tracerdata that shall not be forwarded to
+%% the trace control component, or that the node is actually already part of
+%% another session.
+%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
+remove_nodeparams(Nodes,NodesParams) ->
+ remove_nodeparams_2(Nodes,NodesParams,[],[]).
+
+remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
+ (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
+ Node=element(1,NodeParam),
+ Params=element(2,NodeParam), % This is tracerdata!
+ case lists:member(Node,Nodes) of
+ true -> % Remove this one, in another session.
+ remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
+ false -> % Ok so far...
+ case is_tool_internal_tracerdata(Params) of
+ false -> % Then keep it and use it later!
+ remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
+ true -> % Since it is, remove it from the list.
+ remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
+ end
+ end;
+remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
+ remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
+remove_nodeparams_2(_,[],NPAcc,NAcc) ->
+ {lists:reverse(NPAcc),NAcc}.
+%% ------------------------------------------------------------------------------
+
+%% Help function which adds both the nodes which were already part of another
+%% session and the nodes that we actually did not issue any init_tracing for.
+%% Returns a new Result list of [{Node,NodeResult},...].
+init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
+ NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
+ init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
+
+init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
+ [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
+init_fix_resultnodes_2([],_,Result) ->
+ Result. % Append Result to the end of the list.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions to reactivate.
+%% ------------------------------------------------------------------------------
+
+h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
+ case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
+ {ok,CSuspResults} ->
+ {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
+ lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
+ {[Node|GoodNs],BadNs};
+ ({Node,{error,Reason}},{GoodNs,BadNs})->
+ {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
+ end,
+ {[],[]},
+ CSuspResults),
+ Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
+ {ok,BadResults++Results};
+ {error,Reason} -> % General failure cancelling suspend.
+ {error,{cancel_suspension,Reason}}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function which traverses the list of nodes known to be ours and have
+%% cancelled their suspend. If we fail redoing one of the activities associated
+%% with a node, the node will be reported in the return value as failed. From
+%% that point on its state must be considered unknown since we do not know how
+%% many of the activities were successfully redone.
+h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
+ case get_activities_actstorage(Node,ACTstorage) of
+ {ok,Activities} -> % The node existed in activity storage.
+ {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
+ h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
+ false -> % Node not present in activity storage.
+ h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
+ end;
+h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
+ lists:reverse(Acc).
+
+%% Help function actually redoing the activity. Note that there must be one
+%% clause here for every type of activity.
+%% Returns {NrGoodCmds,NrBadCmds}.
+%% The number of good or bad commands refers to inviso commands done. If any
+%% of the subparts of such a command returned an error, the command is concidered
+%% no good.
+h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
+ {ok,[{_Node,{ok,Answers}}]} ->
+ case h_reactivate_redo_activity_check_tf(Answers) of
+ ok ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
+ error -> % At least oneReports the first encountered error.
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
+ end;
+ {ok,[{_Node,{error,_Reason}}]} ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
+ {error,_Reason} -> % General error when doing cmd.
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
+ end;
+h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
+ {ok,[{_Node,ok}]} ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
+ {ok,[{_Node,{error,_Reason}}]} ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
+ {error,_Reason} -> % General error when doing cmd.
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
+ end;
+h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
+ {Good,Bad}.
+
+%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
+%% to see if there were any errors.
+h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
+ h_reactivate_redo_activity_check_tf(Rest);
+h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
+ error;
+h_reactivate_redo_activity_check_tf([]) ->
+ ok.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions to tp (setting trace patterns, both local and global).
+%% ------------------------------------------------------------------------------
+
+%% Help function which handles both tpl and tp. Note that the non-distributed case
+%% handled with Nodes='all'.
+%% Returns what shall be the reply to the client.
+h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
+ Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
+ h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
+h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
+ CtrlNode=get_ctrlnode_ld(LD),
+ Dbg=get_dbg_ld(LD),
+ SafetyCatches=get_safetycatches_ld(LD),
+ case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
+ {multinode_expansion,NodeMods} ->
+ NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
+ h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
+ {singlenode_expansion,Modules} ->
+ TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
+ h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
+ module ->
+ TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
+ h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
+ wildcard -> % Means do for all modules, no safety.
+ h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+%% Note that this function can never be called in the non-distributed case.
+h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
+ case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
+ {ok,[{Node,Result}]} ->
+ h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
+ {error,Reason} -> % Failure, but don't stop.
+ h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
+ end;
+h_tp_node_by_node(_,_,_,[],Accum) ->
+ {ok,lists:reverse(Accum)}.
+
+%% Help function which does the actual call to the trace control component.
+%% Note that Nodes can be a list of nodes (including a single one) or
+%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
+%% detected by the 'void' CtrlNode.
+%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
+%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
+%% {error,Reason}.
+h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
+ inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
+ case inviso:PatternFunc(TPs) of
+ {ok,Result} -> % A list of [Nr1,Nr2,error,...].
+ {ok,
+ lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
+ {AccNr+N,AccErr};
+ (error,{AccNr,AccErr}) ->
+ {AccNr,AccErr+1}
+ end,
+ {0,0},
+ Result)};
+ {error,Reason} ->
+ {error,{PatternFunc,Reason}}
+ end;
+h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
+ inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
+ {ok,Result} -> % Result is [{Node,Result},...].
+ {ok,
+ lists:map(fun({Node,{ok,Res}})->
+ {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
+ {ok,{AccNr+N,AccErr}};
+ (error,{AccNr,AccErr}) ->
+ {ok,{AccNr,AccErr+1}}
+ end,
+ {ok,{0,0}},
+ Res)};
+ ({_Node,{error,Reason}})->
+ {error,Reason}
+ end,
+ Result)};
+ {error,Reason} ->
+ {error,{PatternFunc,Reason}}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% Help functions for removing trace-patterns.
+%% ------------------------------------------------------------------------------
+
+%% NOT IMPLEMENTED YET.
+h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
+ tbd.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions for calling the trace information facility.
+%% ------------------------------------------------------------------------------
+
+
+%% Function handling the meta trace pattern for capturing registration of local
+%% process names.
+h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
+ {ok,Result} -> % That good we want to modify tpmstorage!
+ NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
+ ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
+ {{ok,ErrorResult++Result},NewACTstorage};
+ {error,Reason} -> % If general failure, do not modify storage.
+ {{error,Reason},ACTstorage}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Functions calling meta trace functions for specified nodes. This function is
+%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
+%% ctpm.
+%% Note that we must store called meta trace functions and their parameters in the
+%% activity storage in order to be able to redo them in case of a reactivate.
+h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
+ {ok,Result} -> % That good we want to modify tpmstorage!
+ NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
+ ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
+ {{ok,ErrorResult++Result},NewACTstorage};
+ {error,Reason} -> % If general failure, do not modify storage.
+ {{error,Reason},ACTstorage}
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions for set trace flags.
+%% ------------------------------------------------------------------------------
+
+%% Help function which sets the tracepatterns in TraceConfList for all nodes
+%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
+%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
+%% and NewACTstorage is traceflag storage modified with the flags added to the
+%% corresponding nodes.
+h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
+ Reply=inviso:tf(TraceConfList),
+ NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
+ {Reply,NewACTstorage};
+h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
+ AllNodes=get_all_session_nodes_rtstates(RTStates),
+ h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
+h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
+ {ok,Result} -> % That good we want to modify actstorage!
+ NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
+ {{ok,Result},NewACTstorage};
+ {error,Reason} -> % If general failure, do not modify actstorage.
+ {{error,Reason},ACTstorage}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% Help functions to stop_session.
+%% ------------------------------------------------------------------------------
+
+%% This function fetches all local log-files using our stored tracerdata. Note
+%% that there are two major ways of tranfering logfiles. Either via distributed
+%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
+%% But there may be info in the RTStates structure about a common file-system.
+%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
+%% nodenames where problems occurred. Note that problems does not necessarily
+%% mean that no files were copied.
+%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
+%% {ti_log,Files}, listing all files successfully fetched. Note that the
+%% list of fetched files contains sublists of filenames. One for each node and
+%% tracerdata.
+%% In the non-distributed system we always use copy (since the files always
+%% resides locally).
+transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
+ if
+ CtrlNode==void -> % When non-distributed, always copy!
+ fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
+ true -> % The distributed case.
+ {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
+ {FailedFetchNodes,FetchedFiles}=
+ case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
+ {ok,Failed,Files} -> % So far no disasters.
+ {Failed,Files};
+ {error,Reason} -> % Means all fetch-nodes failed!
+ inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
+ {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
+ end,
+ {FailedCopyNodes,CopiedFiles}=
+ fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
+ {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
+ end.
+
+%% Help function which finds out which node we have a common file system with
+%% and from which we must make distributed erlang tranfere.
+%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
+find_logfile_transfer_methods(Nodes,RTStates) ->
+ find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
+
+find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
+ {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
+ case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
+ {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
+ find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
+ {value,_} -> % Can't understand dir option.
+ find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
+ false -> % Then we want to use fetch instead.
+ find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
+ end;
+find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
+ {FetchAcc,CopyAcc}.
+%% ------------------------------------------------------------------------------
+
+%% Help function which transferes all local logfiles according to the tracerdata
+%% stored for the nodes in Nodes.
+%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
+%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
+%% or not at all.
+%% FileNames is a list of list of actually fetched files (the name as it is here, including
+%% Dir). The sublists are files which belong together.
+fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
+ LogSpecList=build_logspeclist(Nodes,TRDstorage),
+ case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
+ fetch_log,
+ [LogSpecList,Dir,Prefix])) of
+ {ok,Result} ->
+ Files=get_all_filenames_fetchlog_result(Result,Dbg),
+ FailedNodes=get_all_failednodes_fetchlog_result(Result),
+ {ok,FailedNodes,Files};
+ {error,Reason} -> % Some general failure!
+ {error,{fetch_log,Reason}}
+ end.
+
+%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
+%% Note that there may be more than one tracerdata for a node, resulting in multiple
+%% tuples for that node.
+build_logspeclist(Nodes,TRDstorage) ->
+ build_logspeclist_2(Nodes,TRDstorage,[]).
+
+build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
+ TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
+ build_logspeclist_2(Rest,
+ TRDstorage,
+ [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
+build_logspeclist_2([],_,Acc) ->
+ lists:flatten(Acc).
+
+%% Help function which translates inviso:fetch_log return values to what I
+%% want!
+inviso_fetch_log({error,Reason}) ->
+ {error,Reason};
+inviso_fetch_log({_Success,ResultList}) ->
+ {ok,ResultList}.
+
+%% Help function which collects all filenames mentioned in a noderesult structure.
+%% The files may or may not be complete.
+%% Returns a list of list of filenames. Each sublist contains files which belong
+%% together, i.e because they are a wrap-set.
+get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
+ get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
+
+get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
+ when Success=/=error, list(FileInfo) ->
+ SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
+ get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
+get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
+ inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
+ get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
+get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
+ Accum.
+
+get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
+ FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
+ get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
+get_all_filenames_fetchlog_result_3([],SubAccum) ->
+ SubAccum.
+
+%% Help function which traverses a noderesult and builds a list as return
+%% value containing the nodenames of all nodes not being complete.
+%% Note that a node may occur multiple times since may have fetched logfiles
+%% for several tracerdata from the same node. Makes sure the list contains
+%% unique node names.
+%% Returns a list nodes.
+get_all_failednodes_fetchlog_result(NodeResult) ->
+ get_all_failednodes_fetchlog_result_2(NodeResult,[]).
+
+get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
+ get_all_failednodes_fetchlog_result_2(Rest,Acc);
+get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
+ case lists:member(Node,Acc) of
+ true -> % Already in the list.
+ get_all_failednodes_fetchlog_result_2(Rest,Acc);
+ false -> % Not in Acc, add it!
+ get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
+ end;
+get_all_failednodes_fetchlog_result_2([],Acc) ->
+ Acc.
+%% ------------------------------------------------------------------------------
+
+%% Help function which copies files from one location to Dir and at the same time
+%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
+%% reason the node information is still part of NodeSpecs is that otherwise we can
+%% not report faulty nodes. Note that one node may occur multiple times since there
+%% may be more than one tracerdata for a node.
+%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
+%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
+fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
+ CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
+
+fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
+ case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
+ {0,LocalFiles} -> % Copy went ok and zero errors.
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
+ {_N,LocalFiles} -> % Copied files, but some went wrong.
+ case lists:member(Node,FailedNodes) of
+ true -> % Node already in FailedNodes.
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
+ [{Node,LocalFiles}|Files]);
+ false -> % Node not marked as failed, yet.
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
+ [{Node,LocalFiles}|Files])
+ end
+ end;
+fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
+ {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
+
+fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
+ {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
+ fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
+fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
+ {Errors,Results}.
+
+%% For each file of one file-type (e.g. trace_log).
+fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
+ DestName=Prefix++filename:basename(File),
+ Destination=filename:join(Dir,DestName),
+ case do_copy_file(File,Destination) of
+ ok ->
+ fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
+ {error,Reason} ->
+ inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
+ fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
+ end;
+fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
+ {Errors,LocalFiles}.
+
+%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
+%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
+%% is a complete path to a file to be copied.
+build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
+ build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
+
+%% For each node specified in the NodeSpecList.
+build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
+ TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
+ CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
+ build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
+build_copylist_2(_,_,[],_,Acc) ->
+ lists:flatten(Acc).
+
+%% For each tracerdata found for the node.
+build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
+ case inviso:list_logs(TRD) of
+ {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
+ NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
+ [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
+ {ok,no_log} -> % This tracedata not associated with any log.
+ build_copylist_3(void,Dbg,SourceDir,Node,Rest);
+ {error,Reason} ->
+ inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
+ build_copylist_3(void,Dbg,SourceDir,Node,Rest)
+ end;
+build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
+ case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
+ {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
+ NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
+ [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
+ {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
+ build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
+ {ok,[{Node,{error,Reason}}]} ->
+ inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
+ build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
+ {error,Reason} -> % Some general failure.
+ inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
+ build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
+ end;
+build_copylist_3(_,_,_,_,[]) ->
+ [].
+
+%% Help function which makes a [{Type,Files},...] list where each file in Files
+%% is with full path as found from our file-system.
+build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
+ NewFiles=
+ lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
+ [],
+ Files),
+ build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
+build_copylist_4(_,[],Accum) ->
+ Accum.
+
+
+%% Help function which copies a file using os:cmd.
+%% Returns 'ok' or {error,Reason}.
+do_copy_file(Source,Destination) ->
+ case os:type() of
+ {win32,_} ->
+ os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
+ ok;
+ {unix,_} ->
+ os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
+ ok
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+
+%% ==============================================================================
+%% Various help functions.
+%% ==============================================================================
+
+%% Help function going through the Nodes list and checking that only nodes
+%% mentioned in OurNodes gets returned. It also makes the nodes in the return
+%% value unique.
+remove_nodes_not_ours(Nodes,OurNodes) ->
+ remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
+
+remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
+ case lists:member(Node,OurNodes) of
+ true -> % Ok it is one of our nodes.
+ case lists:member(Node,OurAcc) of
+ true -> % Already in the list, skip.
+ remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
+ false ->
+ remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
+ end;
+ false ->
+ case lists:member(Node,OtherAcc) of
+ true ->
+ remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
+ false ->
+ remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
+ end
+ end;
+remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
+ {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
+%% ------------------------------------------------------------------------------
+
+%% Help function which returns 'true' or 'false' depending on if TracerData is
+%% meant to be used by the session handler (true) or if it supposed to be passed
+%% on to the trace system.
+is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
+ false.
+%% ------------------------------------------------------------------------------
+
+%% Help function which checks that all nodes in the first list of nodes exists
+%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
+%% as one incorrect node was found.
+check_our_nodes([Node|Rest],AllNodes) ->
+ case lists:member(Node,AllNodes) of
+ true ->
+ check_our_nodes(Rest,AllNodes);
+ false -> % Then we can stop right here.
+ false
+ end;
+check_our_nodes([],_) ->
+ true.
+%% ------------------------------------------------------------------------------
+
+%% Help function which checks that a directory actually exists. Returns 'true' or
+%% 'false'.
+check_directory_exists(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok,#file_info{type=directory}} ->
+ true;
+ _ -> % In all other cases it is not valid.
+ false
+ end.
+%% ------------------------------------------------------------------------------
+
+%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
+%% of only tracing runtime components. Not that there will actually be any difference
+%% since the return value does not reflect how stopping the nodes went.
+%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
+stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
+ case inviso:stop_tracing() of
+ {ok,_State} ->
+ ok;
+ {error,Reason} -> % We actually don't care.
+ inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
+ ok
+ end;
+stop_all_tracing(void,_,_) -> % There is no local runtime started.
+ ok;
+stop_all_tracing(CtrlNode,Dbg,Nodes) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
+ {ok,Result} -> % The result is only used for debug.
+ Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
+ (_,Acc)->Acc
+ end,
+ [],
+ Result),
+ if
+ Failed==[] ->
+ ok;
+ true ->
+ inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
+ ok
+ end;
+ {error,Reason} ->
+ {error,{stop_tracing,Reason}}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function removing all local logs using the tracerdata to determine what
+%% logs to remove from where.
+%% There is no significant return value since it is not really clear what to do
+%% if removal went wrong. The function can make debug-reports thought.
+remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
+ LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
+ {ok,Results} ->
+ case look_for_errors_resultlist(Results) of
+ [] -> % No errors found in the result!
+ true;
+ Errors ->
+ inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
+ true
+ end;
+ {error,Reason} -> % Some general error.
+ inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
+ true
+ end.
+
+%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
+%% we must build one tuple for each tracerdata for one node.
+build_logspeclist_remove_logs(Nodes,TRDstorage) ->
+ [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
+%% ------------------------------------------------------------------------------
+
+%% Help function which traverses a resultlist from an inviso function. Such are
+%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
+%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
+%% or {ok,FileName}.
+%% Returns a list of {Node,[{error,Reason},...]}.
+look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
+ [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
+look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
+ case look_for_errors_resultlist_2(NResults,[]) of
+ [] ->
+ look_for_errors_resultlist(Rest);
+ Errors -> % A list of lists.
+ [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
+ end;
+look_for_errors_resultlist([_|Rest]) ->
+ look_for_errors_resultlist(Rest);
+look_for_errors_resultlist([]) ->
+ [].
+
+look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
+ case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
+ [] -> % No errors for this node.
+ look_for_errors_resultlist_2(Rest,Accum);
+ Errors -> % A list of at least one error.
+ look_for_errors_resultlist_2(Rest,[Errors|Accum])
+ end;
+look_for_errors_resultlist_2([],Accum) ->
+ Accum.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the loopdata structure.
+%% Its main purpose is to store information about runtime components participating
+%% in the session and their current status.
+%% ------------------------------------------------------------------------------
+
+-record(ld,{parent,
+ ctrlnode,
+ ctrlpid, % To where to send inviso cmd.
+ rtstates,
+ tracerdata,
+ safetycatches,
+ dbg,
+ actstorage % Activity storage, for reactivate.
+ }).
+
+%% Function creating the initial datastructure.
+%% The datastructure is [{Node,State},...].
+%%
+%% The tracerdata table is a bag simply for the reason that if we try to insert
+%% the same tracerdata for a node twice, we will end up with one tracerdata after
+%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
+%% come as a state-change too.
+mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
+ TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
+ TRDtid=ets:new(TRDtableName,[bag]),
+ ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
+ ACTtid=ets:new(ACTtableName,[bag]),
+ mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
+ #ld{parent=Parent, % The tool main process.
+ ctrlnode=CtrlNode, % Node name where the control component is.
+ ctrlpid=CtrlPid, % The process id of the control component.
+ rtstates=RTStates, % All nodes and their state/status.
+ tracerdata=TRDtid,
+ safetycatches=SafetyCatches,
+ dbg=Dbg,
+ actstorage=ACTtid
+ }.
+
+%% Help function which inserts tracer data for the nodes. Note that we can get
+%% tracer data either from the return value from init_tracing or by asking the
+%% node for it. The latter is necessary for the nodes which were marked not to
+%% be initiated by the session handler. This maybe because those nodes have
+%% autostarted.
+mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
+ mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
+ mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
+
+mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
+ ets:insert(TId,{Node,TracerData}),
+ mk_ld_fill_tracerdata_nodeparams(TId,Rest);
+mk_ld_fill_tracerdata_nodeparams(_,[]) ->
+ ok.
+
+mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
+ ok;
+mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
+ case inviso:get_tracerdata() of
+ {error,_Reason} -> % Perhaps in state new or disconnected.
+ ok; % Do nothing.
+ {ok,TracerData} ->
+ ets:insert(TId,{Node,TracerData})
+ end;
+mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
+ case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
+ {ok,Results} ->
+ mk_ld_fill_tracerdata_othernodes_2(TId,Results);
+ {error,_Reason} -> % Strange, we will probably crash later.
+ ok
+ end.
+
+mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
+ mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
+mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
+ ets:insert(TId,{Node,TracerData}),
+ mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
+mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
+ ok.
+%% ------------------------------------------------------------------------------
+
+get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
+ CtrlNode.
+%% ------------------------------------------------------------------------------
+
+
+get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
+ CtrlPid.
+%% ------------------------------------------------------------------------------
+
+get_rtstates_ld(#ld{rtstates=RTStates}) ->
+ RTStates.
+
+put_rtstates_ld(NewRTStates,LD) ->
+ LD#ld{rtstates=NewRTStates}.
+%% ------------------------------------------------------------------------------
+
+get_trdstorage_ld(#ld{tracerdata=TId}) ->
+ TId.
+
+put_trdstorage_ld(_NewTId,LD) ->
+ LD.
+%% ------------------------------------------------------------------------------
+
+%% Help function which adds the current tracerdata of node Node to the tracerdata
+%% storage. We only want to add tracerdata we have not seen before. We therefore
+%% avoid adding it if the node already is in state ?TRACING.
+%% Returns a new tracerdata (what ever it is)!
+add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
+ case get_statestatus_rtstates(Node,RTStates) of
+ {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
+ TId; % Then do nothing.
+ {ok,_} -> % Since we were not tracing before.
+ case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
+ {ok,TracerData} ->
+ ets:insert(TId,{Node,TracerData});
+ no_tracerdata -> % Strange, how could we become tracing
+ ok;
+ {error,_Reason} -> % The node perhaps disconnected!?
+ ok
+ end;
+ false -> % Very strange, not our node!
+ ok % Do nothing.
+ end.
+
+add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
+ case inviso:get_tracerdata() of
+ {ok,TracerData} ->
+ {ok,TracerData};
+ {error,no_tracerdata} ->
+ no_tracerdata;
+ {error,Reason} ->
+ {error,Reason}
+ end;
+add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
+ {ok,[{Node,{ok,TracerData}}]} ->
+ {ok,TracerData};
+ {ok,[{Node,{error,no_tracerdata}}]} ->
+ no_tracerdata;
+ {ok,[{Node,{error,Reason}}]} ->
+ {error,Reason};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% ------------------------------------------------------------------------------
+
+
+get_safetycatches_ld(#ld{safetycatches=SCs}) ->
+ SCs.
+%% ------------------------------------------------------------------------------
+
+get_dbg_ld(#ld{dbg=Dbg}) ->
+ Dbg.
+%% ------------------------------------------------------------------------------
+
+get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
+ ACTstorage.
+
+put_actstorage_ld(_NewACTstorage,LD) ->
+ LD.
+%% ------------------------------------------------------------------------------
+
+
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the rtstates structure (which is a substructure of loopdata).
+%% It is either:
+%% [{Node,StateStatus,Opts},...]
+%% Node is either the node name of the runtime component erlang node or
+%% ?LOCAL_RUNTIME as returned from the trace control component.
+%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
+%% Status is the returnvalue from trace control component.
+%% i.e: running | {suspended,Reason}
+%% ------------------------------------------------------------------------------
+
+%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
+to_rtstates(ListOfStates) when list(ListOfStates) ->
+ ListOfStates.
+%% ------------------------------------------------------------------------------
+
+%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
+from_rtstates(RTStates) ->
+ RTStates.
+%% ------------------------------------------------------------------------------
+
+%% Function which takes an rtstates structure and a result as returned from
+%% init_tracing. The RTStates is modified for the nodes that changed state as a
+%% result of successful init_tracing.
+%% Returns a new RTStates.
+set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
+ case lists:keysearch(Node,1,Result) of
+ {value,{_,ok}} -> % Means state-change to tracing!
+ [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
+ _ -> % Otherwise, leave it as is.
+ [E|set_tracing_rtstates(Rest,Result)]
+ end;
+set_tracing_rtstates([],_Result) ->
+ [].
+%% ------------------------------------------------------------------------------
+
+%% Function updating the state/status for a certain runtime component.
+%% Returns a new RTStates structure. Note that Node must not necessarily be one
+%% of the nodes in the session. Meaning that Node shall not be added to RTStates
+%% should it not already be in there.
+statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,_,Opts}} ->
+ lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
+ _ -> % Then Node does not exist.
+ RTStates % Just keep it as is, as keyreplace would have done.
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Function updating the state/status for a certain runtime component. The
+%% state/status is set to 'unavailable'.
+%% Returns a new RTStates structure.
+set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,_,Opts}} ->
+ lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
+ _ -> % Then Node does not exist.
+ RTStates % Just keep it as is, as keyreplace would have done.
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Function finding the statestatus associated with Node in the RTStates structure.
+%% Returns {ok,StateStatus} or 'false'.
+get_statestatus_rtstates(Node,RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,StateStatus,_}} ->
+ {ok,StateStatus};
+ false ->
+ false
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function which returns a list of all nodes that are currently marked
+%% as available to us in the runtime state structure.
+get_all_available_nodes_rtstates(RTStates) ->
+ get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
+ (_)->true
+ end,
+ RTStates)).
+%% ------------------------------------------------------------------------------
+
+%% Help function returning a list of all nodes belonging to this session.
+get_all_session_nodes_rtstates(RTStates) ->
+ lists:map(fun({Node,_,_})->Node end,RTStates).
+%% ------------------------------------------------------------------------------
+
+%% Function which returns a list of nodes that are indicated as tracing in the
+%% RTStates structure.
+get_all_tracing_nodes_rtstates(RTStates) ->
+ lists:map(fun({N,_,_})->N end,
+ lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
+%% ------------------------------------------------------------------------------
+
+%% Returns the options associated with Node in the RTStates structure.
+get_opts_rtstates(Node,RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,_,Opts}} ->
+ {ok,Opts};
+ false ->
+ false
+ end.
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the tracerdata structure, which is a part of the loopdata.
+%% The tracerdata structure is an ETS-table of type bag storing:
+%% {Node,TracerData}.
+%% Note that there can of course be multiple entries for a node.
+%% ------------------------------------------------------------------------------
+
+%% Help function which takes a tracerdata loopdata structure and returns a list
+%% of all stored tracerdata for a certain Node.
+find_tracerdata_for_node_trd(Node,TRD) ->
+ case ets:lookup(TRD,Node) of
+ Result when list(Result) ->
+ lists:map(fun({_Node,TracerData})->TracerData end,Result);
+ _ -> % Should probably never happend.
+ []
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the activity storage structure, which is part of the
+%% loopdata. It stores entries about things that needs to be "redone" in case
+%% of a reactivation of the node. The time order is also important.
+%% Note that for every ActivityType there must be a "handler" in the reactivation
+%% functionality.
+%%
+%% The structure is a bag of {Node,ActivityType,What}.
+%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
+%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
+%% /{Op,[]}
+%% TraceConfList=[{Proc,Flags},...]
+%% How=true|false
+%% ------------------------------------------------------------------------------
+
+%% Function that adds meta-pattern activities to the activity storage. Note
+%% that one of the parameters to the function is a return value from an
+%% inviso call. In that way we do not enter activities that were unsuccessful.
+%% Op can be either the setting or clearing of a meta pattern.
+%% Returns a new ACTstorage.
+add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
+ true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
+ add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
+add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
+ add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
+add_tpm_actstorage([],_,_,ACTstorage) ->
+ ACTstorage.
+
+%% Function that adds process trace-flags to the activity storage. Note that one
+%% of the parameters is the return value from an inviso function. Meaning that
+%% if the flags failed in their entirety, no activity will be saved. If only
+%% some of the flags failed, we will not go through the effort of trying to find
+%% out exactly which.
+%% Returns a new activity storage structure.
+add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
+ add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
+add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
+ true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
+ add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
+add_tf_actstorage([],_,_,ACTstorage) ->
+ ACTstorage.
+%% ------------------------------------------------------------------------------
+
+%% Finds all activities associated with Node. Returns a list of them in the
+%% same order as they were inserted.
+get_activities_actstorage(Node,ACTstorage) ->
+ case ets:lookup(ACTstorage,Node) of
+ [] ->
+ false;
+ Result when list(Result) ->
+ {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Function removing all activity entries associated with Node. This is useful
+%% if the Node disconnects for instance.
+del_node_actstorage(Node,ACTstorage) ->
+ ets:delete(ACTstorage,Node),
+ ACTstorage.
+%% ------------------------------------------------------------------------------
+
diff --git a/lib/inviso/test/inviso_tool_SUITE.erl b/lib/inviso/test/inviso_tool_SUITE.erl
index d59e3b5fa8..6b16e506eb 100644
--- a/lib/inviso/test/inviso_tool_SUITE.erl
+++ b/lib/inviso/test/inviso_tool_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
%% properly.
%%
%% Authors:
-%% Lennart �hman, [email protected]
+%% Lennart Öhman, [email protected]
%% -----------------------------------------------------------------------------
-module(inviso_tool_SUITE).
@@ -103,21 +103,21 @@ end_per_testcase(_Case,Config) ->
?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
?l case whereis(inviso_tool) of % In case inviso_tool did not stop.
- Pid when pid(Pid) ->
+ Pid when is_pid(Pid) ->
?l io:format("Had to kill inviso_tool!~n",[]),
?l exit(Pid,kill);
_ ->
true
end,
?l case whereis(inviso_rt) of % In case we ran a runtime here.
- Pid2 when pid(Pid2) ->
+ Pid2 when is_pid(Pid2) ->
?l io:format("Had to kill inviso_rt!~n",[]),
?l exit(Pid2,kill);
_ ->
true
end,
?l case whereis(inviso_c) of % In case we ran the controll component here.
- Pid3 when pid(Pid3) ->
+ Pid3 when is_pid(Pid3) ->
?l io:format("Had to kill inviso_c!~n",[]),
?l exit(Pid3,kill);
_ ->
@@ -196,7 +196,7 @@ end_per_testcase(_Case,Config) ->
%% TEST CASE: Basic, distributed, start of inviso_tool with simple tracing.
dist_basic_1(doc) -> ["Simple test"];
dist_basic_1(suite) -> [];
-dist_basic_1(Config) when list(Config) ->
+dist_basic_1(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=node(),
@@ -325,7 +325,7 @@ dist_basic_1(Config) when list(Config) ->
inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
?l true=check_noderesults(Nodes,
fun({_N,{ok,{[{dependency,infinity}],{tdg,{_M,_F,TDlist}}}}})
- when list(TDlist)->
+ when is_list(TDlist)->
true;
(_) ->
false
@@ -554,7 +554,7 @@ dist_rtc(Config) when is_list(Config) ->
%% This test case tests mainly that reconnect and reinitiations of a node works.
dist_reconnect(doc) -> [""];
dist_reconnect(suite) -> [];
-dist_reconnect(Config) when list(Config) ->
+dist_reconnect(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|OtherNodes]=RemoteNodes,
CNode=node(),
@@ -595,7 +595,7 @@ dist_reconnect(Config) when list(Config) ->
%% than RexExpNode.
?l {ok,NodeResults1}=inviso_tool:inviso(tp,["application.*",module_info,0,[]]),
?l true=check_noderesults(OtherNodes,
- fun({_N,{ok,Ints}}) when list(Ints) ->
+ fun({_N,{ok,Ints}}) when is_list(Ints) ->
NrOfModules=lists:sum(Ints),
true;
(_) ->
@@ -610,9 +610,9 @@ dist_reconnect(Config) when list(Config) ->
%% Now it is time to restart the crashed node and reconnect it and then
%% finally reinitiate it.
- ?l RegExpNodeString=atom_to_list(RegExpNode),
- ?l {match,Pos,1}=regexp:first_match(RegExpNodeString,"@"),
- ?l RegExpNodeName=list_to_atom(lists:sublist(RegExpNodeString,Pos-1)),
+ ?l RegExpNodeString=atom_to_list(RegExpNode),
+ ?l [NodeNameString,_HostNameString] = string:tokens(RegExpNodeString,[$@]),
+ ?l RegExpNodeName=list_to_atom(NodeNameString),
?l test_server:start_node(RegExpNodeName,peer,[]),
?l ok=poll(net_adm,ping,[RegExpNode],pong,20),
?l SuiteDir=filename:dirname(code:which(?MODULE)),
@@ -626,7 +626,7 @@ dist_reconnect(Config) when list(Config) ->
?l ok=poll(rpc,
call,
[RegExpNode,erlang,whereis,[inviso_tool_test_proc]],
- fun(P) when pid(P) -> true;
+ fun(P) when is_pid(P) -> true;
(undefined) -> false
end,
10),
@@ -685,7 +685,7 @@ dist_reconnect(Config) when list(Config) ->
%% mark it as tracing-running.
dist_adopt(doc) -> [""];
dist_adopt(suite) -> [];
-dist_adopt(Config) when list(Config) ->
+dist_adopt(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=node(),
@@ -755,7 +755,7 @@ dist_adopt(Config) when list(Config) ->
%% This test tests that saving and restoring a history works.
dist_history(doc) -> [""];
dist_history(suite) -> [];
-dist_history(Config) when list(Config) ->
+dist_history(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=RegExpNode, % We use a remote control component.
@@ -904,7 +904,7 @@ dist_history(Config) when list(Config) ->
%% are no nodes that can be initiated or reinitiated.
dist_start_session_special(doc) -> [""];
dist_start_session_special(suite) -> [];
-dist_start_session_special(Config) when list(Config) ->
+dist_start_session_special(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=RegExpNode, % We use a remote control component.
@@ -1019,7 +1019,7 @@ stop_inviso_tool_session(CNode,SessionNr,Nodes) ->
%% Help function checking that there is a Result for each node in Nodes.
%% Returns 'true' if successful.
-check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when function(Fun) ->
+check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
case Fun({Node,Result}) of
true ->
case lists:member(Node,Nodes) of
@@ -1052,7 +1052,7 @@ poll(_,_,_,_,0) ->
error;
poll(M,F,Args,Result,Times) ->
try apply(M,F,Args) of
- What when function(Result) ->
+ What when is_function(Result) ->
case Result(What) of
true ->
ok;
diff --git a/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src
index 22ef5178a7..0eff761b61 100644
--- a/lib/mnesia/src/mnesia.appup.src
+++ b/lib/mnesia/src/mnesia.appup.src
@@ -1,12 +1,24 @@
%% -*- erlang -*-
{"%VSN%",
- [
- {"4.4.15",[
- {update, mnesia_dumper, soft, soft_purge, soft_purge, []}
- ]}
+ [
+ {"4.4.16",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia_schema, soft, soft_purge, soft_purge, []}
+ ]},
+ {"4.4.15",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia, soft, soft_purge, soft_purge, []},
+ {update, mnesia_dumper, soft, soft_purge, soft_purge, []}
+ ]}
],
[
+ {"4.4.16",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia_schema, soft, soft_purge, soft_purge, []}
+ ]},
{"4.4.15",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia, soft, soft_purge, soft_purge, []},
{update, mnesia_dumper, soft, soft_purge, soft_purge, []}
]}
]
diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl
index 6cc16c80fd..9e77fe0b9f 100644
--- a/lib/mnesia/src/mnesia_frag.erl
+++ b/lib/mnesia/src/mnesia_frag.erl
@@ -209,7 +209,7 @@ first(ActivityId, Opaque, Tab) ->
end
end.
-search_first(ActivityId, Opaque, Tab, N, FH) when N =< FH#frag_state.n_fragments ->
+search_first(ActivityId, Opaque, Tab, N, FH) when N < FH#frag_state.n_fragments ->
NextN = N + 1,
NextFrag = n_to_frag_name(Tab, NextN),
case mnesia:first(ActivityId, Opaque, NextFrag) of
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index 17e570b881..d1d892a387 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2686,7 +2686,8 @@ do_merge_schema(LockTabs0) ->
if
RemoteRunning /= RemoteRunning1 ->
mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n",
- [node(), RemoteRunning1 -- RemoteRunning]);
+ [node(), RemoteRunning1 -- RemoteRunning]),
+ mnesia:abort({node_not_running, RemoteRunning1 -- RemoteRunning});
true -> ok
end,
NeedsLock = RemoteRunning -- LockedAlready,
@@ -3029,7 +3030,9 @@ announce_im_running([N | Ns], SchemaCs) ->
mnesia_lib:add({current, db_nodes}, N),
mnesia_controller:add_active_replica(schema, N, SchemaCs);
false ->
- ignore
+ mnesia_lib:error("Mnesia on ~p could not connect to node ~p~n",
+ [node(), N]),
+ mnesia:abort({node_not_running, N})
end,
announce_im_running(Ns, SchemaCs);
announce_im_running([], _) ->
diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl
index fe7d366eb3..8ba8427213 100644
--- a/lib/mnesia/test/mnesia_SUITE.erl
+++ b/lib/mnesia/test/mnesia_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,7 +30,7 @@ end_per_testcase(Func, Conf) ->
mnesia_test_lib:end_per_testcase(Func, Conf).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,1}]}]}].
+suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
%% Verify that Mnesia really is a distributed real-time DBMS.
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index 5b52bc6075..5247657b68 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.4.16
+MNESIA_VSN = 4.4.17
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index 077d78bfe5..d61ce940c3 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -472,7 +472,7 @@ static db_result_msg db_connect(byte *args, db_state *state)
&stringlength2ptr, SQL_DRIVER_NOPROMPT);
if (!sql_success(result)) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state));
strcat((char *)diagnos.error_msg,
" Connection to database failed.");
msg = encode_error_message(diagnos.error_msg);
diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
index f1a370d925..2a6667ccd3 100644
--- a/lib/odbc/src/odbc.appup.src
+++ b/lib/odbc/src/odbc.appup.src
@@ -1,8 +1,8 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"2.10.8", [{restart_application, ssl}]}
+ {"2.10.9", [{restart_application, ssl}]}
],
[
- {"2.10.8", [{restart_application, ssl}]}
+ {"2.10.9", [{restart_application, ssl}]}
]}.
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index aacf3924db..42a51be33e 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.9
+ODBC_VSN = 2.10.10
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 91e058f74e..81aedaea56 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -258,7 +258,7 @@
</func>
<func>
- <name> pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name>
+ <name>pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name>
<fsummary> Decodes an asn1 der encoded pkix x509 certificate.</fsummary>
<type>
<v>Cert = der_encoded()</v>
diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src
index 6b6b76d0a5..c65ac7bc99 100644
--- a/lib/public_key/src/public_key.appup.src
+++ b/lib/public_key/src/public_key.appup.src
@@ -1,6 +1,13 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"0.10",
+ [
+ {update, public_key, soft, soft_purge, soft_purge, []},
+ {update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"0.9",
[
{update, public_key, soft, soft_purge, soft_purge, []},
@@ -18,12 +25,19 @@
}
],
[
- {"0.9",
- [
+ {"0.10",
+ [
+ {update, public_key, soft, soft_purge, soft_purge, []},
+ {update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []}
+ ]
+ },
+ {"0.9",
+ [
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_cert, soft, soft_purge, soft_purge, []}
- ]
- },
+ {update, pubkey_cert, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"0.8",
[
{update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []},
@@ -32,5 +46,5 @@
{update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
- }
+ }
]}.
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index 334b9d792e..c99fd6fee1 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.10
+PUBLIC_KEY_VSN = 0.11
diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl
index dfab70b42e..ac7ac2a584 100644
--- a/lib/runtime_tools/src/inviso_rt.erl
+++ b/lib/runtime_tools/src/inviso_rt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1422,7 +1422,17 @@ do_set_trace_patterns(Args,Flags) ->
do_set_trace_patterns_2([{M,F,Arity,MS}|Rest],Flags,Replies) -> % Option-less.
do_set_trace_patterns_2([{M,F,Arity,MS,[]}|Rest],Flags,Replies);
-do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) ->
+do_set_trace_patterns_2(Mlist = [{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) ->
+ case length(Mlist) rem 10 of
+ 0 ->
+ timer:sleep(100);
+ _ ->
+ ok
+ end,
+ %% sleep 100 ms for every 10:th element in the list to let other
+ %% processes run since this is a potentially
+ %% heavy operation that might result in an unresponsive Erlang VM for
+ %% several seconds otherwise
case load_module_on_option(M,Opts) of
true -> % Already present, loaded or no option!
case catch erlang:trace_pattern({M,F,Arity},MS,Flags) of
@@ -1438,30 +1448,11 @@ do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M
do_set_trace_patterns_2(Rest,Flags,[0|Replies])
end;
do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_list(M) ->
- case check_pattern_parameters(void,F,Arity,MS) of % We don't want to repeat bad params.
- ok ->
- case inviso_rt_lib:expand_regexp(M,Opts) of % Get a list of real modulnames.
- Mods when is_list(Mods) ->
- MoreReplies=
- do_set_trace_patterns_2(lists:map(fun(Mod)->
- {Mod,F,Arity,MS,Opts}
- end,
- Mods),
- Flags,
- Replies),
- do_set_trace_patterns_2(Rest,Flags,MoreReplies);
- {error,Reason} ->
- do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies])
- end;
- error -> % Bad pattern parameters.
- do_set_trace_patterns_2(Rest,
- Flags,
- [{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies])
- end;
+ do_set_trace_patterns_2([{{void,M},F,Arity,MS,Opts}|Rest],Flags,Replies);
do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies)
when is_list(Dir),is_list(M) ->
- case check_pattern_parameters(void,F,Arity,MS) of % We don't want to repeat bad params.
- ok ->
+ case check_pattern_parameters('_',F,Arity,MS) of % We don't want to repeat bad params.
+ true ->
case inviso_rt_lib:expand_regexp(Dir,M,Opts) of % Get a list of real modulnames.
Mods when is_list(Mods) ->
MoreReplies=
@@ -1475,7 +1466,7 @@ do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies)
{error,Reason} ->
do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies])
end;
- error -> % Bad pattern parameters.
+ false -> % Bad pattern parameters.
do_set_trace_patterns_2(Rest,
Flags,
[{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies])
@@ -2174,21 +2165,20 @@ check_flags_2([Faulty|_],_Flags) -> {error,{bad_flag,Faulty}}.
%% the function is to avoid to get multiple error return values in the return
%% list for a pattern used together with a regexp expanded module name.
check_pattern_parameters(Mod,Func,Arity,MS) ->
- if
- (Mod=='_') and (Func=='_') and (Arity=='_') and
- (is_list(MS) or (MS==true) or (MS==false)) ->
- ok;
- (is_atom(Mod) and (Mod/='_')) and (Func=='_') and (Arity=='_') and
- (is_list(MS) or (MS==true) or (MS==false)) ->
- ok;
- (is_atom(Mod) and (Mod/='_')) and
- (is_atom(Func) and (Func/='_')) and
- ((Arity=='_') or is_integer(Arity)) and
- (is_list(MS) or (MS==true) or (MS==false)) ->
- ok;
- true ->
- error
- end.
+ MSresult = check_MS(MS),
+ MFAresult = check_MFA(Mod,Func,Arity),
+ MFAresult and MSresult.
+
+check_MS(MS) when is_list(MS) -> true;
+check_MS(true) -> true;
+check_MS(false) -> true.
+
+check_MFA('_','_','_') -> true;
+check_MFA(Mod,'_','_') when is_atom(Mod) -> true;
+check_MFA(Mod,'_',A) when is_atom(Mod), is_integer(A) -> false;
+check_MFA(Mod,F,'_') when is_atom(Mod), is_atom(F) -> true;
+check_MFA(Mod,F,A) when is_atom(Mod), is_atom(F), is_integer(A) -> true.
+
%% -----------------------------------------------------------------------------
%% Help function finding out if Mod is loaded, and if not, if it can successfully
diff --git a/lib/snmp/doc/src/snmpc.xml b/lib/snmp/doc/src/snmpc.xml
index 57f00ff8b5..771629492d 100644
--- a/lib/snmp/doc/src/snmpc.xml
+++ b/lib/snmp/doc/src/snmpc.xml
@@ -31,7 +31,7 @@
<rev></rev>
<file>snmpc.xml</file>
</header>
- <module>snmpc(module)</module>
+ <module>snmpc</module>
<modulesummary>Interface Functions to the SNMP toolkit MIB compiler</modulesummary>
<description>
<p>The module <c>snmpc</c> contains interface functions to the
diff --git a/lib/snmp/doc/src/snmpc_cmd.xml b/lib/snmp/doc/src/snmpc_cmd.xml
index 18ff71631c..9358382a10 100644
--- a/lib/snmp/doc/src/snmpc_cmd.xml
+++ b/lib/snmp/doc/src/snmpc_cmd.xml
@@ -31,7 +31,7 @@
<rev></rev>
<file>snmpc_cmd.xml</file>
</header>
- <com>snmpc</com>
+ <com>snmpc(command)</com>
<comsummary>SNMP MIB compiler frontend</comsummary>
<description>
<p>The <c><![CDATA[snmpc]]></c> program provides a way to run
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index daf7b77527..cd5c9281cd 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -269,6 +269,13 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
<p> {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, unknown_ca}, {bad_cert, name_not_permitted}, {bad_cert, missing_basic_constraint}, {bad_cert, invalid_key_usage}</p>
</item>
+ <tag>{hibernate_after, integer()|undefined}</tag>
+ <item>When an integer-value is specified, the <code>ssl_connection</code>
+ will go into hibernation after the specified number of milliseconds
+ of inactivity, thus reducing its memory footprint. When
+ <code>undefined</code> is specified (this is the default), the process
+ will never go into hibernation.
+ </item>
</taglist>
</section>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index e6a8c557fc..d3e426f254 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,12 +1,14 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"4.1.3", [{restart_application, ssl}]},
{"4.1.2", [{restart_application, ssl}]},
{"4.1.1", [{restart_application, ssl}]},
{"4.1", [{restart_application, ssl}]},
{"4.0.1", [{restart_application, ssl}]}
],
[
+ {"4.1.3", [{restart_application, ssl}]},
{"4.1.2", [{restart_application, ssl}]},
{"4.1.1", [{restart_application, ssl}]},
{"4.1", [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index b85188b878..3512e194bc 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -60,7 +60,7 @@
{keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} |
{cacertfile, path()} | {dh, der_encoded()} | {dhfile, path()} |
{ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
- {reuse_session, fun()}.
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined}.
-type verify_type() :: verify_none | verify_peer.
-type path() :: string().
@@ -711,7 +711,8 @@ handle_options(Opts0, _Role) ->
reuse_sessions = handle_option(reuse_sessions, Opts, true),
secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
- debug = handle_option(debug, Opts, [])
+ debug = handle_option(debug, Opts, []),
+ hibernate_after = handle_option(hibernate_after, Opts, undefined)
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -720,7 +721,7 @@ handle_options(Opts0, _Role) ->
depth, cert, certfile, key, keyfile,
password, cacerts, cacertfile, dh, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cb_info, renegotiate_at, secure_renegotiate],
+ cb_info, renegotiate_at, secure_renegotiate, hibernate_after],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -827,6 +828,10 @@ validate_option(renegotiate_at, Value) when is_integer(Value) ->
validate_option(debug, Value) when is_list(Value); Value == true ->
Value;
+validate_option(hibernate_after, undefined) ->
+ undefined;
+validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {eoptions, {Opt, Value}}}).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 85245f4342..574e1e9468 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -289,9 +289,9 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
%% gen_fsm callbacks
%%====================================================================
%%--------------------------------------------------------------------
--spec init(list()) -> {ok, state_name(), #state{}} | {stop, term()}.
+-spec init(list()) -> {ok, state_name(), #state{}, timeout()} | {stop, term()}.
%% Possible return values not used now.
-%% | {ok, state_name(), #state{}, timeout()} |
+%% | {ok, state_name(), #state{}} |
%% ignore
%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
%% gen_fsm:start_link/3,4, this function is called by the new process to
@@ -311,7 +311,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
session_cache = CacheRef,
private_key = Key,
diffie_hellman_params = DHParams},
- {ok, hello, State}
+ {ok, hello, State, get_timeout(State)}
catch
throw:Error ->
{stop, Error}
@@ -412,6 +412,9 @@ hello(Hello = #client_hello{client_version = ClientVersion},
{stop, normal, State}
end;
+hello(timeout, State) ->
+ { next_state, hello, State, hibernate };
+
hello(Msg, State) ->
handle_unexpected_message(Msg, hello, State).
%%--------------------------------------------------------------------
@@ -460,6 +463,9 @@ abbreviated(#finished{verify_data = Data} = Finished,
{stop, normal, State}
end;
+abbreviated(timeout, State) ->
+ { next_state, abbreviated, State, hibernate };
+
abbreviated(Msg, State) ->
handle_unexpected_message(Msg, abbreviated, State).
@@ -582,6 +588,9 @@ certify(#client_key_exchange{exchange_keys = Keys},
{stop, normal, State}
end;
+certify(timeout, State) ->
+ { next_state, certify, State, hibernate };
+
certify(Msg, State) ->
handle_unexpected_message(Msg, certify, State).
@@ -664,6 +673,9 @@ cipher(#finished{verify_data = Data} = Finished,
{stop, normal, State}
end;
+cipher(timeout, State) ->
+ { next_state, cipher, State, hibernate };
+
cipher(Msg, State) ->
handle_unexpected_message(Msg, cipher, State).
@@ -693,6 +705,9 @@ connection(#hello_request{}, #state{host = Host, port = Port,
connection(#client_hello{} = Hello, #state{role = server} = State) ->
hello(Hello, State);
+connection(timeout, State) ->
+ {next_state, connection, State, hibernate};
+
connection(Msg, State) ->
handle_unexpected_message(Msg, connection, State).
%%--------------------------------------------------------------------
@@ -705,7 +720,7 @@ connection(Msg, State) ->
%% the event. Not currently used!
%%--------------------------------------------------------------------
handle_event(_Event, StateName, State) ->
- {next_state, StateName, State}.
+ {next_state, StateName, State, get_timeout(State)}.
%%--------------------------------------------------------------------
-spec handle_sync_event(term(), from(), state_name(), #state{}) ->
@@ -736,7 +751,8 @@ handle_sync_event({application_data, Data0}, From, connection,
{Msgs, [], ConnectionStates} ->
Result = Transport:send(Socket, Msgs),
{reply, Result,
- connection, State#state{connection_states = ConnectionStates}};
+ connection, State#state{connection_states = ConnectionStates},
+ get_timeout(State)};
{Msgs, RestData, ConnectionStates} ->
if
Msgs =/= [] ->
@@ -749,12 +765,14 @@ handle_sync_event({application_data, Data0}, From, connection,
renegotiation = {true, internal}})
end
catch throw:Error ->
- {reply, Error, connection, State}
+ {reply, Error, connection, State, get_timeout(State)}
end;
handle_sync_event({application_data, Data}, From, StateName,
#state{send_queue = Queue} = State) ->
%% In renegotiation priorities handshake, send data when handshake is finished
- {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}};
+ {next_state, StateName,
+ State#state{send_queue = queue:in({From, Data}, Queue)},
+ get_timeout(State)};
handle_sync_event(start, From, hello, State) ->
hello(start, State#state{from = From});
@@ -768,9 +786,9 @@ handle_sync_event(start, From, hello, State) ->
%% here to make sure it is the users problem and not owers if
%% they upgrade a active socket.
handle_sync_event(start, _, connection, State) ->
- {reply, connected, connection, State};
+ {reply, connected, connection, State, get_timeout(State)};
handle_sync_event(start, From, StateName, State) ->
- {next_state, StateName, State#state{from = From}};
+ {next_state, StateName, State#state{from = From}, get_timeout(State)};
handle_sync_event(close, _, StateName, State) ->
%% Run terminate before returning
@@ -796,7 +814,7 @@ handle_sync_event({shutdown, How0}, _, StateName,
case Transport:shutdown(Socket, How0) of
ok ->
- {reply, ok, StateName, State};
+ {reply, ok, StateName, State, get_timeout(State)};
Error ->
{stop, normal, Error, State}
end;
@@ -807,30 +825,33 @@ handle_sync_event({recv, N}, From, connection = StateName, State0) ->
%% Doing renegotiate wait with handling request until renegotiate is
%% finished. Will be handled by next_state_connection/2.
handle_sync_event({recv, N}, From, StateName, State) ->
- {next_state, StateName, State#state{bytes_to_read = N, from = From,
- recv_during_renegotiation = true}};
+ {next_state, StateName,
+ State#state{bytes_to_read = N, from = From,
+ recv_during_renegotiation = true},
+ get_timeout(State)};
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
NewMon = erlang:monitor(process, User),
erlang:demonitor(OldMon, [flush]),
- {reply, ok, StateName, State#state{user_application = {NewMon,User}}};
+ {reply, ok, StateName, State#state{user_application = {NewMon,User}},
+ get_timeout(State)};
handle_sync_event({get_opts, OptTags}, _From, StateName,
#state{socket = Socket,
socket_options = SockOpts} = State) ->
OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []),
- {reply, OptsReply, StateName, State};
+ {reply, OptsReply, StateName, State, get_timeout(State)};
handle_sync_event(sockname, _From, StateName,
#state{socket = Socket} = State) ->
SockNameReply = inet:sockname(Socket),
- {reply, SockNameReply, StateName, State};
+ {reply, SockNameReply, StateName, State, get_timeout(State)};
handle_sync_event(peername, _From, StateName,
#state{socket = Socket} = State) ->
PeerNameReply = inet:peername(Socket),
- {reply, PeerNameReply, StateName, State};
+ {reply, PeerNameReply, StateName, State, get_timeout(State)};
handle_sync_event({set_opts, Opts0}, _From, StateName,
#state{socket_options = Opts1,
@@ -840,27 +861,27 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
State1 = State0#state{socket_options = Opts},
if
Opts#socket_options.active =:= false ->
- {reply, ok, StateName, State1};
+ {reply, ok, StateName, State1, get_timeout(State1)};
Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
%% Need data, set active once
{Record, State2} = next_record_if_active(State1),
case next_state(StateName, Record, State2) of
- {next_state, StateName, State} ->
- {reply, ok, StateName, State};
+ {next_state, StateName, State, Timeout} ->
+ {reply, ok, StateName, State, Timeout};
{stop, Reason, State} ->
{stop, Reason, State}
end;
Buffer =:= <<>> ->
%% Active once already set
- {reply, ok, StateName, State1};
+ {reply, ok, StateName, State1, get_timeout(State1)};
true ->
case application_data(<<>>, State1) of
Stop = {stop,_,_} ->
Stop;
{Record, State2} ->
case next_state(StateName, Record, State2) of
- {next_state, StateName, State} ->
- {reply, ok, StateName, State};
+ {next_state, StateName, State, Timeout} ->
+ {reply, ok, StateName, State, Timeout};
{stop, Reason, State} ->
{stop, Reason, State}
end
@@ -871,7 +892,7 @@ handle_sync_event(renegotiate, From, connection, State) ->
renegotiate(State#state{renegotiation = {true, From}});
handle_sync_event(renegotiate, _, StateName, State) ->
- {reply, {error, already_renegotiating}, StateName, State};
+ {reply, {error, already_renegotiating}, StateName, State, get_timeout(State)};
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
@@ -879,19 +900,19 @@ handle_sync_event(info, _, StateName,
AtomVersion = ssl_record:protocol_version(Version),
{reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}},
- StateName, State};
+ StateName, State, get_timeout(State)};
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
{cipher_suite, ssl_cipher:suite_definition(Suite)}],
- StateName, State};
+ StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
- {reply, {ok, Cert}, StateName, State}.
+ {reply, {ok, Cert}, StateName, State, get_timeout(State)}.
%%--------------------------------------------------------------------
-spec handle_info(msg(),state_name(), #state{}) ->
@@ -955,7 +976,7 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _,
handle_info(Msg, StateName, State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
error_logger:info_report(Report),
- {next_state, StateName, State}.
+ {next_state, StateName, State, get_timeout(State)}.
%%--------------------------------------------------------------------
-spec terminate(reason(), state_name(), #state{}) -> term().
@@ -1778,7 +1799,7 @@ handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet]} = State)
handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]} = State0) ->
FsmReturn = {next_state, StateName, State0#state{tls_packets = Packets}},
case Handle(Packet, FsmReturn) of
- {next_state, NextStateName, State} ->
+ {next_state, NextStateName, State, _Timeout} ->
handle_tls_handshake(Handle, NextStateName, State);
{stop, _,_} = Stop ->
Stop
@@ -1789,11 +1810,11 @@ next_state(_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
{stop, normal, State};
next_state(Next, no_record, State) ->
- {next_state, Next, State};
+ {next_state, Next, State, get_timeout(State)};
next_state(Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
Alerts = decode_alerts(EncAlerts),
- handle_alerts(Alerts, {next_state, Next, State});
+ handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
State0 = #state{tls_handshake_buffer = Buf0, negotiated_version = Version}) ->
@@ -2044,7 +2065,7 @@ handle_alerts([], Result) ->
handle_alerts(_, {stop, _, _} = Stop) ->
%% If it is a fatal alert immediately close
Stop;
-handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
@@ -2225,3 +2246,8 @@ linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) ->
end;
linux_workaround_transport_delivery_problems(_, _) ->
ok.
+
+get_timeout(#state{ssl_options=#ssl_options{hibernate_after=undefined}}) ->
+ infinity;
+get_timeout(#state{ssl_options=#ssl_options{hibernate_after=HibernateAfter}}) ->
+ HibernateAfter.
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 715941e3ad..c28daa271e 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -98,7 +98,11 @@
reuse_sessions, % boolean()
renegotiate_at,
secure_renegotiate,
- debug %
+ debug,
+ hibernate_after % undefined if not hibernating,
+ % or number of ms of inactivity
+ % after which ssl_connection will
+ % go into hibernation
}).
-record(socket_options,
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 823401c863..fd3b6d06ad 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -60,6 +60,7 @@ ERL_FILES = $(MODULES:%=%.erl)
HRL_FILES = ssl_test_MACHINE.hrl
HRL_FILES_SRC = \
+ ssl_int.hrl \
ssl_alert.hrl \
ssl_handshake.hrl
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 8495ddb1d7..4f0907027f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -29,6 +29,7 @@
-include_lib("public_key/include/public_key.hrl").
-include("ssl_alert.hrl").
+-include("ssl_int.hrl").
-define('24H_in_sec', 86400).
-define(TIMEOUT, 60000).
@@ -250,7 +251,9 @@ all() ->
unknown_server_ca_accept_backwardscompatibilty,
%%different_ca_peer_sign,
no_reuses_session_server_restart_new_cert,
- no_reuses_session_server_restart_new_cert_file, reuseaddr].
+ no_reuses_session_server_restart_new_cert_file, reuseaddr,
+ hibernate
+ ].
groups() ->
[].
@@ -3319,6 +3322,45 @@ reuseaddr(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
+
+hibernate(doc) ->
+ ["Check that an SSL connection that is started with option "
+ "{hibernate_after, 1000} indeed hibernates after 1000ms of "
+ "inactivity"];
+
+hibernate(suite) ->
+ [];
+
+hibernate(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {Client, #sslsocket{pid=Pid}} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{hibernate_after, 1000}|ClientOpts]}]),
+
+ { current_function, { _M, _F, _A } } =
+ process_info(Pid, current_function),
+
+ timer:sleep(1100),
+
+ { current_function, { erlang, hibernate, 3} } =
+ process_info(Pid, current_function),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
send_recv_result(Socket) ->
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index f6ccbe85e3..8c639aa312 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -128,12 +128,14 @@ remove_close_msg(ReconnectTimes) ->
remove_close_msg(ReconnectTimes -1)
end.
-
start_client(Args) ->
- Result = spawn_link(?MODULE, run_client, [Args]),
+ Result = spawn_link(?MODULE, run_client, [lists:delete(return_socket, Args)]),
receive
- connected ->
- Result
+ { connected, Socket } ->
+ case lists:member(return_socket, Args) of
+ true -> { Result, Socket };
+ false -> Result
+ end
end.
run_client(Opts) ->
@@ -145,7 +147,7 @@ run_client(Opts) ->
test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
case rpc:call(Node, ssl, connect, [Host, Port, Options]) of
{ok, Socket} ->
- Pid ! connected,
+ Pid ! { connected, Socket },
test_server:format("Client: connected~n", []),
%% In specail cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index a4be7bb889..2f1edfa186 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1,2 +1 @@
-
-SSL_VSN = 4.1.3
+SSL_VSN = 4.1.4
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 494ef55f59..67197c80cb 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -70,7 +70,12 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-init_per_testcase(TC, Config) when TC =:= misc; TC =:= compile ->
+init_per_testcase(TC, Config) when TC =:= misc;
+ TC =:= compile;
+ TC =:= analyse;
+ TC =:= distribution;
+ TC =:= otp_5031;
+ TC =:= stop ->
case code:which(crypto) of
Path when is_list(Path) ->
init_per_testcase(dummy_tc, Config);
@@ -366,6 +371,7 @@ distribution(Config) when is_list(Config) ->
%% Check that stop() unloads on all nodes
?line ok = cover:stop(),
+ ?line timer:sleep(100), %% Give nodes time to unload on slow machines.
?line LocalBeam = code:which(f),
?line N2Beam = rpc:call(N2,code,which,[f]),
?line true = is_unloaded(LocalBeam),
diff --git a/lib/typer/RELEASE_NOTES b/lib/typer/RELEASE_NOTES
new file mode 100644
index 0000000000..d91a815ee9
--- /dev/null
+++ b/lib/typer/RELEASE_NOTES
@@ -0,0 +1,22 @@
+==============================================================================
+ Major features, additions and changes between Typer versions
+ (in reversed chronological order)
+==============================================================================
+
+Version 0.9 (in Erlang/OTP R14B02)
+----------------------------------
+ - Major rewrite; all code has been cleaned up and placed in one file.
+ The only reason why this is not version 1.0 yet is that there is no proper
+ documentation for typer which can be displayed in the www.erlang.org site.
+ - Added ability to receive the set of exported types and report unknown ones.
+ - Better handling of overloaded contracts; especially erroneous ones on which
+ typer does not crash anymore.
+ - Fixed problem that caused typer to hang when given a file whose module name
+ did not correspond to the file name.
+ - Added two undocumented options that may come very handy when trying to
+ understand why typer reports some particular set of types for the functions
+ in a module. These options are mainly for typer developers at this point,
+ but may become documented in some future version.
+
+Older versions
+--------------
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
index 9c9ef6156f..3d7827b5b5 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -45,15 +45,9 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES = \
- typer \
- typer_annotator \
- typer_info \
- typer_map \
- typer_options \
- typer_preprocess
-
-HRL_FILES= typer.hrl
+MODULES = typer
+
+HRL_FILES=
ERL_FILES= $(MODULES:%=%.erl)
INSTALL_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
TARGET_FILES= $(INSTALL_FILES)
@@ -87,8 +81,8 @@ clean:
# Special Build Targets
# ----------------------------------------------------
-$(EBIN)/typer_options.$(EMULATOR): typer_options.erl ../vsn.mk Makefile
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer_options.erl
+$(EBIN)/typer.$(EMULATOR): typer.erl ../vsn.mk Makefile
+ erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer.erl
$(APP_TARGET): $(APP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
@@ -97,14 +91,9 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
# ---------------------------------------------------------------------
-# dependencies -- I wish they were somehow automatically generated
+# dependencies
# ---------------------------------------------------------------------
-$(EBIN)/typer.beam: typer.hrl
-$(EBIN)/typer_annotator.beam: typer.hrl
-$(EBIN)/typer_info.beam: typer.hrl
-$(EBIN)/typer_options.beam: typer.hrl
-$(EBIN)/typer_preprocess.beam: typer.hrl
# ----------------------------------------------------
# Release Target
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
index 3eb0cbf816..850829e1dc 100644
--- a/lib/typer/src/typer.app.src
+++ b/lib/typer/src/typer.app.src
@@ -3,12 +3,7 @@
{application, typer,
[{description, "TYPe annotator for ERlang programs, version %VSN%"},
{vsn, "%VSN%"},
- {modules, [typer,
- typer_annotator,
- typer_info,
- typer_map,
- typer_options,
- typer_preprocess]},
+ {modules, [typer]},
{registered, []},
{applications, [compiler, dialyzer, hipe, kernel, stdlib]},
{env, []}]}.
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index e19614f911..fc8caa4f21 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -1,65 +1,107 @@
%% -*- erlang-indent-level: 2 -*-
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-%%--------------------------------------------------------------------
+%%-----------------------------------------------------------------------
%% File : typer.erl
-%% Author : Bingwen He <[email protected]>
-%% Description : The main driver of the TypEr application
-%%--------------------------------------------------------------------
+%% Author(s) : The first version of typer was written by Bingwen He
+%% with guidance from Kostis Sagonas and Tobias Lindahl.
+%% Since June 2008 typer is maintained by Kostis Sagonas.
+%% Description : An Erlang/OTP application that shows type information
+%% for Erlang modules to the user. Additionally, it can
+%% annotate the code of files with such type information.
+%%-----------------------------------------------------------------------
-module(typer).
-%% Avoid warning for local function error/1 clashing with autoimported BIF.
--compile({no_auto_import,[error/1]}).
-export([start/0]).
--export([error/1, compile_error/1]). % for error reporting
--include("typer.hrl").
+%%-----------------------------------------------------------------------
+
+-define(SHOW, show).
+-define(SHOW_EXPORTED, show_exported).
+-define(ANNOTATE, annotate).
+-define(ANNOTATE_INC_FILES, annotate_inc_files).
+
+-type mode() :: ?SHOW | ?SHOW_EXPORTED | ?ANNOTATE | ?ANNOTATE_INC_FILES.
+
+%%-----------------------------------------------------------------------
+
+-type files() :: [file:filename()].
+-type callgraph() :: dialyzer_callgraph:callgraph().
+-type codeserver() :: dialyzer_codeserver:codeserver().
+-type plt() :: dialyzer_plt:plt().
+
+-record(analysis,
+ {mode :: mode() | 'undefined',
+ macros = [] :: [{atom(), term()}],
+ includes = [] :: files(),
+ codeserver = dialyzer_codeserver:new():: codeserver(),
+ callgraph = dialyzer_callgraph:new() :: callgraph(),
+ files = [] :: files(), % absolute names
+ plt = none :: 'none' | file:filename(),
+ no_spec = false :: boolean(),
+ show_succ = false :: boolean(),
+ %% For choosing between specs or edoc @spec comments
+ edoc = false :: boolean(),
+ %% Files in 'fms' are compilable with option 'to_pp'; we keep them
+ %% as {FileName, ModuleName} in case the ModuleName is different
+ fms = [] :: [{file:filename(), module()}],
+ ex_func = map__new() :: map(),
+ record = map__new() :: map(),
+ func = map__new() :: map(),
+ inc_func = map__new() :: map(),
+ trust_plt = dialyzer_plt:new() :: plt()}).
+-type analysis() :: #analysis{}.
+
+-record(args, {files = [] :: files(),
+ files_r = [] :: files(),
+ trusted = [] :: files()}).
+-type args() :: #args{}.
%%--------------------------------------------------------------------
-spec start() -> no_return().
start() ->
- {Args, Analysis} = typer_options:process(),
+ {Args, Analysis} = process_cl_args(),
%% io:format("Args: ~p\n", [Args]),
%% io:format("Analysis: ~p\n", [Analysis]),
- TrustedFiles = typer_preprocess:get_all_files(Args, trust),
- Analysis1 = Analysis#typer_analysis{t_files = TrustedFiles},
- Analysis2 = extract(Analysis1),
- All_Files = typer_preprocess:get_all_files(Args, analysis),
+ TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1),
+ Analysis2 = extract(Analysis, TrustedFiles),
+ All_Files = get_all_files(Args),
%% io:format("All_Files: ~p\n", [All_Files]),
- Analysis3 = Analysis2#typer_analysis{ana_files = All_Files},
- Analysis4 = typer_info:collect(Analysis3),
- %% io:format("Final: ~p\n", [Analysis4#typer_analysis.final_files]),
+ Analysis3 = Analysis2#analysis{files = All_Files},
+ Analysis4 = collect_info(Analysis3),
+ %% io:format("Final: ~p\n", [Analysis4#analysis.fms]),
TypeInfo = get_type_info(Analysis4),
- typer_annotator:annotate(TypeInfo),
+ show_or_annotate(TypeInfo),
%% io:format("\nTyper analysis finished\n"),
erlang:halt(0).
%%--------------------------------------------------------------------
--spec extract(#typer_analysis{}) -> #typer_analysis{}.
+-spec extract(analysis(), files()) -> analysis().
-extract(#typer_analysis{macros = Macros, includes = Includes,
- t_files = TFiles, trust_plt = TrustPLT} = Analysis) ->
+extract(#analysis{macros = Macros,
+ includes = Includes,
+ trust_plt = TrustPLT} = Analysis, TrustedFiles) ->
%% io:format("--- Extracting trusted typer_info... "),
Ds = [{d, Name, Value} || {Name, Value} <- Macros],
CodeServer = dialyzer_codeserver:new(),
@@ -87,7 +129,7 @@ extract(#typer_analysis{macros = Macros, includes = Includes,
{error, Reason} -> compile_error(Reason)
end
end,
- CodeServer1 = lists:foldl(Fun, CodeServer, TFiles),
+ CodeServer1 = lists:foldl(Fun, CodeServer, TrustedFiles),
%% Process remote types
NewCodeServer =
try
@@ -112,30 +154,30 @@ extract(#typer_analysis{macros = Macros, includes = Includes,
dialyzer_plt:insert_contract_list(TmpPlt, SpecList)
end,
NewTrustPLT = lists:foldl(FoldFun, TrustPLT, Modules),
- Analysis#typer_analysis{trust_plt = NewTrustPLT}.
+ Analysis#analysis{trust_plt = NewTrustPLT}.
%%--------------------------------------------------------------------
--spec get_type_info(#typer_analysis{}) -> #typer_analysis{}.
+-spec get_type_info(analysis()) -> analysis().
-get_type_info(#typer_analysis{callgraph = CallGraph,
- trust_plt = TrustPLT,
- code_server = CodeServer} = Analysis) ->
+get_type_info(#analysis{callgraph = CallGraph,
+ trust_plt = TrustPLT,
+ codeserver = CodeServer} = Analysis) ->
StrippedCallGraph = remove_external(CallGraph, TrustPLT),
%% io:format("--- Analyzing callgraph... "),
try
NewPlt = dialyzer_succ_typings:analyze_callgraph(StrippedCallGraph,
TrustPLT, CodeServer),
- Analysis#typer_analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt}
+ Analysis#analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt}
catch
error:What ->
- error(io_lib:format("Analysis failed with message: ~p",
- [{What, erlang:get_stacktrace()}]));
+ fatal_error(io_lib:format("Analysis failed with message: ~p",
+ [{What, erlang:get_stacktrace()}]));
throw:{dialyzer_succ_typing_error, Msg} ->
- error(io_lib:format("Analysis failed with message: ~s", [Msg]))
+ fatal_error(io_lib:format("Analysis failed with message: ~s", [Msg]))
end.
--spec remove_external(dialyzer_callgraph:callgraph(), dialyzer_plt:plt()) -> dialyzer_callgraph:callgraph().
+-spec remove_external(callgraph(), plt()) -> callgraph().
remove_external(CallGraph, PLT) ->
{StrippedCG0, Ext} = dialyzer_callgraph:remove_external(CallGraph),
@@ -143,11 +185,16 @@ remove_external(CallGraph, PLT) ->
case get_external(Ext, PLT) of
[] -> ok;
Externals ->
- msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)]))
+ msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)])),
+ ExtTypes = rcv_ext_types(),
+ case ExtTypes of
+ [] -> ok;
+ _ -> msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes]))
+ end
end,
StrippedCG.
--spec get_external([{mfa(), mfa()}], dialyzer_plt:plt()) -> [mfa()].
+-spec get_external([{mfa(), mfa()}], plt()) -> [mfa()].
get_external(Exts, Plt) ->
Fun = fun ({_From, To = {M, F, A}}, Acc) ->
@@ -163,31 +210,782 @@ get_external(Exts, Plt) ->
lists:foldl(Fun, [], Exts).
%%--------------------------------------------------------------------
+%% Showing type information or annotating files with such information.
+%%--------------------------------------------------------------------
+
+-define(TYPER_ANN_DIR, "typer_ann").
+
+-type line() :: non_neg_integer().
+-type fa() :: {atom(), arity()}.
+-type func_info() :: {line(), atom(), arity()}.
+
+-record(info, {records = map__new() :: map(),
+ functions = [] :: [func_info()],
+ types = map__new() :: map(),
+ edoc = false :: boolean()}).
+-record(inc, {map = map__new() :: map(), filter = [] :: files()}).
+-type inc() :: #inc{}.
+
+-spec show_or_annotate(analysis()) -> 'ok'.
+
+show_or_annotate(#analysis{mode = Mode, fms = Files} = Analysis) ->
+ case Mode of
+ ?SHOW -> show(Analysis);
+ ?SHOW_EXPORTED -> show(Analysis);
+ ?ANNOTATE ->
+ Fun = fun ({File, Module}) ->
+ Info = get_final_info(File, Module, Analysis),
+ write_typed_file(File, Info)
+ end,
+ lists:foreach(Fun, Files);
+ ?ANNOTATE_INC_FILES ->
+ IncInfo = write_and_collect_inc_info(Analysis),
+ write_inc_files(IncInfo)
+ end.
+
+write_and_collect_inc_info(Analysis) ->
+ Fun = fun ({File, Module}, Inc) ->
+ Info = get_final_info(File, Module, Analysis),
+ write_typed_file(File, Info),
+ IncFuns = get_functions(File, Analysis),
+ collect_imported_functions(IncFuns, Info#info.types, Inc)
+ end,
+ NewInc = lists:foldl(Fun, #inc{}, Analysis#analysis.fms),
+ clean_inc(NewInc).
+
+write_inc_files(Inc) ->
+ Fun =
+ fun (File) ->
+ Val = map__lookup(File, Inc#inc.map),
+ %% Val is function with its type info
+ %% in form [{{Line,F,A},Type}]
+ Functions = [Key || {Key, _} <- Val],
+ Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val],
+ Info = #info{types = map__from_list(Val1),
+ records = map__new(),
+ %% Note we need to sort functions here!
+ functions = lists:keysort(1, Functions)},
+ %% io:format("Types ~p\n", [Info#info.types]),
+ %% io:format("Functions ~p\n", [Info#info.functions]),
+ %% io:format("Records ~p\n", [Info#info.records]),
+ write_typed_file(File, Info)
+ end,
+ lists:foreach(Fun, dict:fetch_keys(Inc#inc.map)).
+
+show(Analysis) ->
+ Fun = fun ({File, Module}) ->
+ Info = get_final_info(File, Module, Analysis),
+ show_type_info(File, Info)
+ end,
+ lists:foreach(Fun, Analysis#analysis.fms).
+
+get_final_info(File, Module, Analysis) ->
+ Records = get_records(File, Analysis),
+ Types = get_types(Module, Analysis, Records),
+ Functions = get_functions(File, Analysis),
+ Edoc = Analysis#analysis.edoc,
+ #info{records = Records, functions = Functions, types = Types, edoc = Edoc}.
+
+collect_imported_functions(Functions, Types, Inc) ->
+ %% Coming from other sourses, including:
+ %% FIXME: How to deal with yecc-generated file????
+ %% --.yrl (yecc-generated file)???
+ %% -- yeccpre.hrl (yecc-generated file)???
+ %% -- other cases
+ Fun = fun ({File, _} = Obj, I) ->
+ case is_yecc_gen(File, I) of
+ {true, NewI} -> NewI;
+ {false, NewI} ->
+ check_imported_functions(Obj, NewI, Types)
+ end
+ end,
+ lists:foldl(Fun, Inc, Functions).
+
+-spec is_yecc_gen(file:filename(), inc()) -> {boolean(), inc()}.
+
+is_yecc_gen(File, #inc{filter = Fs} = Inc) ->
+ case lists:member(File, Fs) of
+ true -> {true, Inc};
+ false ->
+ case filename:extension(File) of
+ ".yrl" ->
+ Rootname = filename:rootname(File, ".yrl"),
+ Obj = Rootname ++ ".erl",
+ case lists:member(Obj, Fs) of
+ true -> {true, Inc};
+ false ->
+ NewInc = Inc#inc{filter = [Obj|Fs]},
+ {true, NewInc}
+ end;
+ _ ->
+ case filename:basename(File) of
+ "yeccpre.hrl" -> {true, Inc};
+ _ -> {false, Inc}
+ end
+ end
+ end.
+
+check_imported_functions({File, {Line, F, A}}, Inc, Types) ->
+ IncMap = Inc#inc.map,
+ FA = {F, A},
+ Type = get_type_info(FA, Types),
+ case map__lookup(File, IncMap) of
+ none -> %% File is not added. Add it
+ Obj = {File,[{FA, {Line, Type}}]},
+ NewMap = map__insert(Obj, IncMap),
+ Inc#inc{map = NewMap};
+ Val -> %% File is already in. Check.
+ case lists:keyfind(FA, 1, Val) of
+ false ->
+ %% Function is not in; add it
+ Obj = {File, Val ++ [{FA, {Line, Type}}]},
+ NewMap = map__insert(Obj, IncMap),
+ Inc#inc{map = NewMap};
+ Type ->
+ %% Function is in and with same type
+ Inc;
+ _ ->
+ %% Function is in but with diff type
+ inc_warning(FA, File),
+ Elem = lists:keydelete(FA, 1, Val),
+ NewMap = case Elem of
+ [] -> map__remove(File, IncMap);
+ _ -> map__insert({File, Elem}, IncMap)
+ end,
+ Inc#inc{map = NewMap}
+ end
+ end.
+
+inc_warning({F, A}, File) ->
+ io:format(" ***Warning: Skip function ~p/~p ", [F, A]),
+ io:format("in file ~p because of inconsistent type\n", [File]).
+
+clean_inc(Inc) ->
+ Inc1 = remove_yecc_generated_file(Inc),
+ normalize_obj(Inc1).
+
+remove_yecc_generated_file(#inc{filter = Filter} = Inc) ->
+ Fun = fun (Key, #inc{map = Map} = I) ->
+ I#inc{map = map__remove(Key, Map)}
+ end,
+ lists:foldl(Fun, Inc, Filter).
+
+normalize_obj(TmpInc) ->
+ Fun = fun (Key, Val, Inc) ->
+ NewVal = [{{Line,F,A},Type} || {{F,A},{Line,Type}} <- Val],
+ map__insert({Key, NewVal}, Inc)
+ end,
+ TmpInc#inc{map = map__fold(Fun, map__new(), TmpInc#inc.map)}.
+
+get_records(File, Analysis) ->
+ map__lookup(File, Analysis#analysis.record).
+
+get_types(Module, Analysis, Records) ->
+ TypeInfoPlt = Analysis#analysis.trust_plt,
+ TypeInfo =
+ case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of
+ none -> [];
+ {value, List} -> List
+ end,
+ CodeServer = Analysis#analysis.codeserver,
+ TypeInfoList =
+ case Analysis#analysis.show_succ of
+ true ->
+ [convert_type_info(I) || I <- TypeInfo];
+ false ->
+ [get_type(I, CodeServer, Records) || I <- TypeInfo]
+ end,
+ map__from_list(TypeInfoList).
+
+convert_type_info({{_M, F, A}, Range, Arg}) ->
+ {{F, A}, {Range, Arg}}.
+
+get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) ->
+ case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of
+ error ->
+ {{F, A}, {Range, Arg}};
+ {ok, {_FileLine, Contract}} ->
+ Sig = erl_types:t_fun(Arg, Range),
+ case dialyzer_contracts:check_contract(Contract, Sig) of
+ ok -> {{F, A}, {contract, Contract}};
+ {error, {extra_range, _, _}} ->
+ {{F, A}, {contract, Contract}};
+ {error, {overlapping_contract, []}} ->
+ {{F, A}, {contract, Contract}};
+ {error, invalid_contract} ->
+ CString = dialyzer_contracts:contract_to_string(Contract),
+ SigString = dialyzer_utils:format_sig(Sig, Records),
+ Msg = io_lib:format("Error in contract of function ~w:~w/~w\n"
+ "\t The contract is: " ++ CString ++ "\n" ++
+ "\t but the inferred signature is: ~s",
+ [M, F, A, SigString]),
+ fatal_error(Msg);
+ {error, ErrorStr} when is_list(ErrorStr) -> % ErrorStr is a string()
+ Msg = io_lib:format("Error in contract of function ~w:~w/~w: ~s",
+ [M, F, A, ErrorStr]),
+ fatal_error(Msg)
+ end
+ end.
+
+get_functions(File, Analysis) ->
+ case Analysis#analysis.mode of
+ ?SHOW ->
+ Funcs = map__lookup(File, Analysis#analysis.func),
+ Inc_Funcs = map__lookup(File, Analysis#analysis.inc_func),
+ remove_module_info(Funcs) ++ normalize_incFuncs(Inc_Funcs);
+ ?SHOW_EXPORTED ->
+ Ex_Funcs = map__lookup(File, Analysis#analysis.ex_func),
+ remove_module_info(Ex_Funcs);
+ ?ANNOTATE ->
+ Funcs = map__lookup(File, Analysis#analysis.func),
+ remove_module_info(Funcs);
+ ?ANNOTATE_INC_FILES ->
+ map__lookup(File, Analysis#analysis.inc_func)
+ end.
+
+normalize_incFuncs(Functions) ->
+ [FunInfo || {_FileName, FunInfo} <- Functions].
+
+-spec remove_module_info([func_info()]) -> [func_info()].
+
+remove_module_info(FunInfoList) ->
+ F = fun ({_,module_info,0}) -> false;
+ ({_,module_info,1}) -> false;
+ ({Line,F,A}) when is_integer(Line), is_atom(F), is_integer(A) -> true
+ end,
+ lists:filter(F, FunInfoList).
+
+write_typed_file(File, Info) ->
+ io:format(" Processing file: ~p\n", [File]),
+ Dir = filename:dirname(File),
+ RootName = filename:basename(filename:rootname(File)),
+ Ext = filename:extension(File),
+ TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR),
+ TmpNewFilename = lists:concat([RootName, ".ann", Ext]),
+ NewFileName = filename:join(TyperAnnDir, TmpNewFilename),
+ case file:make_dir(TyperAnnDir) of
+ {error, Reason} ->
+ case Reason of
+ eexist -> %% TypEr dir exists; remove old typer files
+ ok = file:delete(NewFileName),
+ write_typed_file(File, Info, NewFileName);
+ enospc ->
+ Msg = io_lib:format("Not enough space in ~p\n", [Dir]),
+ fatal_error(Msg);
+ eacces ->
+ Msg = io:format("No write permission in ~p\n", [Dir]),
+ fatal_error(Msg);
+ _ ->
+ Msg = io_lib:format("Unhandled error ~s when writing ~p\n",
+ [Reason, Dir]),
+ fatal_error(Msg)
+ end;
+ ok -> %% Typer dir does NOT exist
+ write_typed_file(File, Info, NewFileName)
+ end.
+
+write_typed_file(File, Info, NewFileName) ->
+ {ok, Binary} = file:read_file(File),
+ Chars = binary_to_list(Binary),
+ write_typed_file(Chars, NewFileName, Info, 1, []),
+ io:format(" Saved as: ~p\n", [NewFileName]).
+
+write_typed_file(Chars, File, #info{functions = []}, _LNo, _Acc) ->
+ ok = file:write_file(File, list_to_binary(Chars), [append]);
+write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) ->
+ [{Line,F,A}|RestFuncs] = Info#info.functions,
+ case Line of
+ 1 -> %% This will happen only for inc files
+ ok = raw_write(F, A, Info, File, []),
+ NewInfo = Info#info{functions = RestFuncs},
+ NewAcc = [],
+ write_typed_file(Chars, File, NewInfo, Line, NewAcc);
+ _ ->
+ case Ch of
+ 10 ->
+ NewLineNo = LineNo + 1,
+ {NewInfo, NewAcc} =
+ case NewLineNo of
+ Line ->
+ ok = raw_write(F, A, Info, File, [Ch|Acc]),
+ {Info#info{functions = RestFuncs}, []};
+ _ ->
+ {Info, [Ch|Acc]}
+ end,
+ write_typed_file(Chs, File, NewInfo, NewLineNo, NewAcc);
+ _ ->
+ write_typed_file(Chs, File, Info, LineNo, [Ch|Acc])
+ end
+ end.
+
+raw_write(F, A, Info, File, Content) ->
+ TypeInfo = get_type_string(F, A, Info, file),
+ ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n",
+ ContentBin = list_to_binary(ContentList),
+ file:write_file(File, ContentBin, [append]).
+
+get_type_string(F, A, Info, Mode) ->
+ Type = get_type_info({F,A}, Info#info.types),
+ TypeStr =
+ case Type of
+ {contract, C} ->
+ dialyzer_contracts:contract_to_string(C);
+ {RetType, ArgType} ->
+ Sig = erl_types:t_fun(ArgType, RetType),
+ dialyzer_utils:format_sig(Sig, Info#info.records)
+ end,
+ case Info#info.edoc of
+ false ->
+ case {Mode, Type} of
+ {file, {contract, _}} -> "";
+ _ ->
+ Prefix = lists:concat(["-spec ", F]),
+ lists:concat([Prefix, TypeStr, "."])
+ end;
+ true ->
+ Prefix = lists:concat(["%% @spec ", F]),
+ lists:concat([Prefix, TypeStr, "."])
+ end.
+
+show_type_info(File, Info) ->
+ io:format("\n%% File: ~p\n%% ", [File]),
+ OutputString = lists:concat(["~.", length(File)+8, "c~n"]),
+ io:fwrite(OutputString, [$-]),
+ Fun = fun ({_LineNo, F, A}) ->
+ TypeInfo = get_type_string(F, A, Info, show),
+ io:format("~s\n", [TypeInfo])
+ end,
+ lists:foreach(Fun, Info#info.functions).
+
+get_type_info(Func, Types) ->
+ case map__lookup(Func, Types) of
+ none ->
+ %% Note: Typeinfo of any function should exist in
+ %% the result offered by dialyzer, otherwise there
+ %% *must* be something wrong with the analysis
+ Msg = io_lib:format("No type info for function: ~p\n", [Func]),
+ fatal_error(Msg);
+ {contract, _Fun} = C -> C;
+ {_RetType, _ArgType} = RA -> RA
+ end.
+
+%%--------------------------------------------------------------------
+%% Processing of command-line options and arguments.
+%%--------------------------------------------------------------------
+
+-spec process_cl_args() -> {args(), analysis()}.
+
+process_cl_args() ->
+ ArgList = init:get_plain_arguments(),
+ %% io:format("Args is ~p\n", [ArgList]),
+ {Args, Analysis} = analyze_args(ArgList, #args{}, #analysis{}),
+ %% if the mode has not been set, set it to the default mode (show)
+ {Args, case Analysis#analysis.mode of
+ undefined -> Analysis#analysis{mode = ?SHOW};
+ Mode when is_atom(Mode) -> Analysis
+ end}.
+
+analyze_args([], Args, Analysis) ->
+ {Args, Analysis};
+analyze_args(ArgList, Args, Analysis) ->
+ {Result, Rest} = cl(ArgList),
+ {NewArgs, NewAnalysis} = analyze_result(Result, Args, Analysis),
+ analyze_args(Rest, NewArgs, NewAnalysis).
+
+cl(["-h"|_]) -> help_message();
+cl(["--help"|_]) -> help_message();
+cl(["-v"|_]) -> version_message();
+cl(["--version"|_]) -> version_message();
+cl(["--edoc"|Opts]) -> {edoc, Opts};
+cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts};
+cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
+cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
+cl(["--show_success_typings"|Opts]) -> {show_succ, Opts};
+cl(["--show-success-typings"|Opts]) -> {show_succ, Opts};
+cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts};
+cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts};
+cl(["--no_spec"|Opts]) -> {no_spec, Opts};
+cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts};
+cl(["-D"++Def|Opts]) ->
+ case Def of
+ "" -> fatal_error("no variable name specified after -D");
+ _ ->
+ DefPair = process_def_list(re:split(Def, "=", [{return, list}])),
+ {{def, DefPair}, Opts}
+ end;
+cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts};
+cl(["-I"++Dir|Opts]) ->
+ case Dir of
+ "" -> fatal_error("no include directory specified after -I");
+ _ -> {{inc, Dir}, Opts}
+ end;
+cl(["-T"|Opts]) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ case Files of
+ [] -> fatal_error("no file or directory specified after -T");
+ [_|_] -> {{trusted, Files}, RestOpts}
+ end;
+cl(["-r"|Opts]) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ {{files_r, Files}, RestOpts};
+cl(["-"++H|_]) -> fatal_error("unknown option -"++H);
+cl(Opts) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ {{files, Files}, RestOpts}.
+
+process_def_list(L) ->
+ case L of
+ [Name, Value] ->
+ {ok, Tokens, _} = erl_scan:string(Value ++ "."),
+ {ok, ErlValue} = erl_parse:parse_term(Tokens),
+ {list_to_atom(Name), ErlValue};
+ [Name] ->
+ {list_to_atom(Name), true}
+ end.
+
+%% Get information about files that the user trusts and wants to analyze
+analyze_result({files, Val}, Args, Analysis) ->
+ NewVal = Args#args.files ++ Val,
+ {Args#args{files = NewVal}, Analysis};
+analyze_result({files_r, Val}, Args, Analysis) ->
+ NewVal = Args#args.files_r ++ Val,
+ {Args#args{files_r = NewVal}, Analysis};
+analyze_result({trusted, Val}, Args, Analysis) ->
+ NewVal = Args#args.trusted ++ Val,
+ {Args#args{trusted = NewVal}, Analysis};
+analyze_result(edoc, Args, Analysis) ->
+ {Args, Analysis#analysis{edoc = true}};
+%% Get useful information for actual analysis
+analyze_result({mode, Mode}, Args, Analysis) ->
+ case Analysis#analysis.mode of
+ undefined -> {Args, Analysis#analysis{mode = Mode}};
+ OldMode -> mode_error(OldMode, Mode)
+ end;
+analyze_result({def, Val}, Args, Analysis) ->
+ NewVal = Analysis#analysis.macros ++ [Val],
+ {Args, Analysis#analysis{macros = NewVal}};
+analyze_result({inc, Val}, Args, Analysis) ->
+ NewVal = Analysis#analysis.includes ++ [Val],
+ {Args, Analysis#analysis{includes = NewVal}};
+analyze_result({plt, Plt}, Args, Analysis) ->
+ {Args, Analysis#analysis{plt = Plt}};
+analyze_result(show_succ, Args, Analysis) ->
+ {Args, Analysis#analysis{show_succ = true}};
+analyze_result(no_spec, Args, Analysis) ->
+ {Args, Analysis#analysis{no_spec = true}}.
+
+%%--------------------------------------------------------------------
+%% File processing.
+%%--------------------------------------------------------------------
+
+-spec get_all_files(args()) -> [file:filename(),...].
+
+get_all_files(#args{files = Fs, files_r = Ds}) ->
+ case filter_fd(Fs, Ds, fun test_erl_file_exclude_ann/1) of
+ [] -> fatal_error("no file(s) to analyze");
+ AllFiles -> AllFiles
+ end.
--spec error(string()) -> no_return().
+-spec test_erl_file_exclude_ann(file:filename()) -> boolean().
-error(Slogan) ->
+test_erl_file_exclude_ann(File) ->
+ case is_erl_file(File) of
+ true -> %% Exclude files ending with ".ann.erl"
+ case re:run(File, "[\.]ann[\.]erl$") of
+ {match, _} -> false;
+ nomatch -> true
+ end;
+ false -> false
+ end.
+
+-spec is_erl_file(file:filename()) -> boolean().
+
+is_erl_file(File) ->
+ filename:extension(File) =:= ".erl".
+
+-type test_file_fun() :: fun((file:filename()) -> boolean()).
+
+-spec filter_fd(files(), files(), test_file_fun()) -> files().
+
+filter_fd(File_Dir, Dir_R, Fun) ->
+ All_File_1 = process_file_and_dir(File_Dir, Fun),
+ All_File_2 = process_dir_rec(Dir_R, Fun),
+ remove_dup(All_File_1 ++ All_File_2).
+
+-spec process_file_and_dir(files(), test_file_fun()) -> files().
+
+process_file_and_dir(File_Dir, TestFun) ->
+ Fun =
+ fun (Elem, Acc) ->
+ case filelib:is_regular(Elem) of
+ true -> process_file(Elem, TestFun, Acc);
+ false -> check_dir(Elem, false, Acc, TestFun)
+ end
+ end,
+ lists:foldl(Fun, [], File_Dir).
+
+-spec process_dir_rec(files(), test_file_fun()) -> files().
+
+process_dir_rec(Dirs, TestFun) ->
+ Fun = fun (Dir, Acc) -> check_dir(Dir, true, Acc, TestFun) end,
+ lists:foldl(Fun, [], Dirs).
+
+-spec check_dir(file:filename(), boolean(), files(), test_file_fun()) -> files().
+
+check_dir(Dir, Recursive, Acc, Fun) ->
+ case file:list_dir(Dir) of
+ {ok, Files} ->
+ {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir),
+ case Recursive of
+ false ->
+ FinalFiles = process_file_and_dir(TmpFiles, Fun),
+ Acc ++ FinalFiles;
+ true ->
+ TmpAcc1 = process_file_and_dir(TmpFiles, Fun),
+ TmpAcc2 = process_dir_rec(TmpDirs, Fun),
+ Acc ++ TmpAcc1 ++ TmpAcc2
+ end;
+ {error, eacces} ->
+ fatal_error("no access permission to dir \""++Dir++"\"");
+ {error, enoent} ->
+ fatal_error("cannot access "++Dir++": No such file or directory");
+ {error, _Reason} ->
+ fatal_error("error involving a use of file:list_dir/1")
+ end.
+
+%% Same order as the input list
+-spec process_file(file:filename(), test_file_fun(), files()) -> files().
+
+process_file(File, TestFun, Acc) ->
+ case TestFun(File) of
+ true -> Acc ++ [File];
+ false -> Acc
+ end.
+
+%% Same order as the input list
+-spec split_dirs_and_files(files(), file:filename()) -> {files(), files()}.
+
+split_dirs_and_files(Elems, Dir) ->
+ Test_Fun =
+ fun (Elem, {DirAcc, FileAcc}) ->
+ File = filename:join(Dir, Elem),
+ case filelib:is_regular(File) of
+ false -> {[File|DirAcc], FileAcc};
+ true -> {DirAcc, [File|FileAcc]}
+ end
+ end,
+ {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems),
+ {lists:reverse(Dirs), lists:reverse(Files)}.
+
+%% Removes duplicate filenames but keeps the order of the input list
+-spec remove_dup(files()) -> files().
+
+remove_dup(Files) ->
+ Test_Dup = fun (File, Acc) ->
+ case lists:member(File, Acc) of
+ true -> Acc;
+ false -> [File|Acc]
+ end
+ end,
+ Reversed_Elems = lists:foldl(Test_Dup, [], Files),
+ lists:reverse(Reversed_Elems).
+
+%%--------------------------------------------------------------------
+%% Collect information.
+%%--------------------------------------------------------------------
+
+-type inc_file_info() :: {file:filename(), func_info()}.
+
+-record(tmpAcc, {file :: file:filename(),
+ module :: atom(),
+ funcAcc = [] :: [func_info()],
+ incFuncAcc = [] :: [inc_file_info()],
+ dialyzerObj = [] :: [{mfa(), {_, _}}]}).
+
+-spec collect_info(analysis()) -> analysis().
+
+collect_info(Analysis) ->
+ NewPlt =
+ try get_dialyzer_plt(Analysis) of
+ DialyzerPlt ->
+ dialyzer_plt:merge_plts([Analysis#analysis.trust_plt, DialyzerPlt])
+ catch
+ throw:{dialyzer_error,_Reason} ->
+ fatal_error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
+ end,
+ NewAnalysis = lists:foldl(fun collect_one_file_info/2,
+ Analysis#analysis{trust_plt = NewPlt},
+ Analysis#analysis.files),
+ %% Process Remote Types
+ TmpCServer = NewAnalysis#analysis.codeserver,
+ NewCServer =
+ try
+ NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
+ NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer),
+ OldRecords = dialyzer_plt:get_types(NewPlt),
+ OldExpTypes = dialyzer_plt:get_exported_types(NewPlt),
+ MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ MergedExpTypes = sets:union(NewExpTypes, OldExpTypes),
+ %% io:format("Merged Records ~p",[MergedRecords]),
+ TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
+ TmpCServer2 =
+ dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
+ TmpCServer1),
+ TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3)
+ catch
+ throw:{error, ErrorMsg} ->
+ fatal_error(ErrorMsg)
+ end,
+ NewAnalysis#analysis{codeserver = NewCServer}.
+
+collect_one_file_info(File, Analysis) ->
+ Ds = [{d,Name,Val} || {Name,Val} <- Analysis#analysis.macros],
+ %% Current directory should also be included in "Includes".
+ Includes = [filename:dirname(File)|Analysis#analysis.includes],
+ Is = [{i,Dir} || Dir <- Includes],
+ Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
+ case dialyzer_utils:get_abstract_code_from_src(File, Options) of
+ {error, Reason} ->
+ %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
+ compile_error(Reason);
+ {ok, AbstractCode} ->
+ case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
+ error -> compile_error(["Could not get core erlang for "++File]);
+ {ok, Core} ->
+ case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, Records} ->
+ Mod = cerl:concrete(cerl:module_name(Core)),
+ case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, SpecInfo} ->
+ ExpTypes = get_exported_types_from_core(Core),
+ analyze_core_tree(Core, Records, SpecInfo, ExpTypes,
+ Analysis, File)
+ end
+ end
+ end
+ end.
+
+analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) ->
+ Module = cerl:concrete(cerl:module_name(Core)),
+ TmpTree = cerl:from_records(Core),
+ CS1 = Analysis#analysis.codeserver,
+ NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
+ {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
+ CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
+ CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
+ CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
+ CS5 =
+ case Analysis#analysis.no_spec of
+ true -> CS4;
+ false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4)
+ end,
+ OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CS5),
+ MergedExpTypes = sets:union(ExpTypes, OldExpTypes),
+ CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5),
+ Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
+ TmpCG = Analysis#analysis.callgraph,
+ CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
+ Fun = fun analyze_one_function/2,
+ All_Defs = cerl:module_defs(Tree),
+ Acc = lists:foldl(Fun, #tmpAcc{file = File, module = Module}, All_Defs),
+ Exported_FuncMap = map__insert({File, Ex_Funcs}, Analysis#analysis.ex_func),
+ %% we must sort all functions in the file which
+ %% originate from this file by *numerical order* of lineNo
+ Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
+ FuncMap = map__insert({File, Sorted_Functions}, Analysis#analysis.func),
+ %% we do not need to sort functions which are imported from included files
+ IncFuncMap = map__insert({File, Acc#tmpAcc.incFuncAcc},
+ Analysis#analysis.inc_func),
+ FMs = Analysis#analysis.fms ++ [{File, Module}],
+ RecordMap = map__insert({File, Records}, Analysis#analysis.record),
+ Analysis#analysis{fms = FMs,
+ callgraph = CG,
+ codeserver = CS6,
+ ex_func = Exported_FuncMap,
+ inc_func = IncFuncMap,
+ record = RecordMap,
+ func = FuncMap}.
+
+analyze_one_function({Var, FunBody} = Function, Acc) ->
+ F = cerl:fname_id(Var),
+ A = cerl:fname_arity(Var),
+ TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
+ NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
+ [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
+ BaseName = filename:basename(FileName),
+ FuncInfo = {LineNo, F, A},
+ OriginalName = Acc#tmpAcc.file,
+ {FuncAcc, IncFuncAcc} =
+ case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
+ true -> %% Coming from original file
+ %% io:format("Added function ~p\n", [{LineNo, F, A}]),
+ {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
+ false ->
+ %% Coming from other sourses, including:
+ %% -- .yrl (yecc-generated file)
+ %% -- yeccpre.hrl (yecc-generated file)
+ %% -- other cases
+ {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
+ end,
+ Acc#tmpAcc{funcAcc = FuncAcc,
+ incFuncAcc = IncFuncAcc,
+ dialyzerObj = NewDialyzerObj}.
+
+-spec get_dialyzer_plt(analysis()) -> plt().
+
+get_dialyzer_plt(#analysis{plt = PltFile0}) ->
+ PltFile =
+ case PltFile0 =:= none of
+ true -> dialyzer_plt:get_default_plt();
+ false -> PltFile0
+ end,
+ dialyzer_plt:from_file(PltFile).
+
+%% Exported Types
+
+get_exported_types_from_core(Core) ->
+ Attrs = cerl:module_attrs(Core),
+ ExpTypes1 = [cerl:concrete(L2) || {L1, L2} <- Attrs,
+ cerl:is_literal(L1),
+ cerl:is_literal(L2),
+ cerl:concrete(L1) =:= 'export_type'],
+ ExpTypes2 = lists:flatten(ExpTypes1),
+ M = cerl:atom_val(cerl:module_name(Core)),
+ sets:from_list([{M, F, A} || {F, A} <- ExpTypes2]).
+
+%%--------------------------------------------------------------------
+%% Utilities for error reporting.
+%%--------------------------------------------------------------------
+
+-spec fatal_error(string()) -> no_return().
+
+fatal_error(Slogan) ->
msg(io_lib:format("typer: ~s\n", [Slogan])),
erlang:halt(1).
-%%--------------------------------------------------------------------
+-spec mode_error(mode(), mode()) -> no_return().
+
+mode_error(OldMode, NewMode) ->
+ Msg = io_lib:format("Mode was previously set to '~s'; "
+ "can not set it to '~s' now",
+ [OldMode, NewMode]),
+ fatal_error(Msg).
-spec compile_error([string()]) -> no_return().
compile_error(Reason) ->
JoinedString = lists:flatten([X ++ "\n" || X <- Reason]),
Msg = "Analysis failed with error report:\n" ++ JoinedString,
- error(Msg).
-
-%%--------------------------------------------------------------------
-%% Outputs a message on 'stderr', if possible.
-%%--------------------------------------------------------------------
+ fatal_error(Msg).
-spec msg(string()) -> 'ok'.
msg(Msg) ->
case os:type() of
- {unix, _} ->
+ {unix, _} -> % Output a message on 'stderr', if possible
P = open_port({fd, 0, 2}, [out]),
port_command(P, Msg),
true = port_close(P),
@@ -197,3 +995,106 @@ msg(Msg) ->
end.
%%--------------------------------------------------------------------
+%% Version and help messages.
+%%--------------------------------------------------------------------
+
+-spec version_message() -> no_return().
+
+version_message() ->
+ io:format("TypEr version "++?VSN++"\n"),
+ erlang:halt(0).
+
+-spec help_message() -> no_return().
+
+help_message() ->
+ S = <<" Usage: typer [--help] [--version] [--plt PLT] [--edoc]
+ [--show | --show-exported | --annotate | --annotate-inc-files]
+ [-Ddefine]* [-I include_dir]* [-T application]* [-r] file*
+
+ Options:
+ -r dir*
+ search directories recursively for .erl files below them
+ --show
+ Prints type specifications for all functions on stdout.
+ (this is the default behaviour; this option is not really needed)
+ --show-exported (or --show_exported)
+ Same as --show, but prints specifications for exported functions only
+ Specs are displayed sorted alphabetically on the function's name
+ --annotate
+ Annotates the specified files with type specifications
+ --annotate-inc-files
+ Same as --annotate but annotates all -include() files as well as
+ all .erl files (use this option with caution - has not been tested much)
+ --edoc
+ Prints type information as Edoc @spec comments, not as type specs
+ --plt PLT
+ Use the specified dialyzer PLT file rather than the default one
+ -T file*
+ The specified file(s) already contain type specifications and these
+ are to be trusted in order to print specs for the rest of the files
+ (Multiple files or dirs, separated by spaces, can be specified.)
+ -Dname (or -Dname=value)
+ pass the defined name(s) to TypEr
+ (The syntax of defines is the same as that used by \"erlc\".)
+ -I include_dir
+ pass the include_dir to TypEr
+ (The syntax of includes is the same as that used by \"erlc\".)
+ --version (or -v)
+ prints the Typer version and exits
+ --help (or -h)
+ prints this message and exits
+
+ Note:
+ * denotes that multiple occurrences of these options are possible.
+">>,
+ io:put_chars(S),
+ erlang:halt(0).
+
+%%--------------------------------------------------------------------
+%% Handle messages.
+%%--------------------------------------------------------------------
+
+rcv_ext_types() ->
+ Self = self(),
+ Self ! {Self, done},
+ rcv_ext_types(Self, []).
+
+rcv_ext_types(Self, ExtTypes) ->
+ receive
+ {Self, ext_types, ExtType} ->
+ rcv_ext_types(Self, [ExtType|ExtTypes]);
+ {Self, done} ->
+ lists:usort(ExtTypes)
+ end.
+
+%%--------------------------------------------------------------------
+%% A convenient abstraction of a Key-Value mapping data structure
+%% specialized for the uses in this module
+%%--------------------------------------------------------------------
+
+-type map() :: dict().
+
+-spec map__new() -> map().
+map__new() ->
+ dict:new().
+
+-spec map__insert({term(), term()}, map()) -> map().
+map__insert(Object, Map) ->
+ {Key, Value} = Object,
+ dict:store(Key, Value, Map).
+
+-spec map__lookup(term(), map()) -> term().
+map__lookup(Key, Map) ->
+ try dict:fetch(Key, Map) catch error:_ -> none end.
+
+-spec map__from_list([{fa(), term()}]) -> map().
+map__from_list(List) ->
+ dict:from_list(List).
+
+-spec map__remove(term(), map()) -> map().
+map__remove(Key, Dict) ->
+ dict:erase(Key, Dict).
+
+-spec map__fold(fun((term(), term(), term()) -> map()), map(), map()) -> map().
+map__fold(Fun, Acc0, Dict) ->
+ dict:fold(Fun, Acc0, Dict).
diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl
deleted file mode 100644
index c331dd82db..0000000000
--- a/lib/typer/src/typer.hrl
+++ /dev/null
@@ -1,64 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--define(SHOW, show).
--define(SHOW_EXPORTED, show_exported).
--define(ANNOTATE, annotate).
--define(ANNOTATE_INC_FILES, annotate_inc_files).
-
--type mode() :: ?SHOW | ?SHOW_EXPORTED | ?ANNOTATE | ?ANNOTATE_INC_FILES.
-
--record(typer_analysis,
- {mode :: mode(),
- macros = [] :: [{atom(), _}], % {macro_name, value}
- includes = [] :: [string()],
-
- %% Esp for Dialyzer
- %% ----------------------
- code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(),
- callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(),
- ana_files = [] :: [string()], % absolute filenames
- plt = none :: 'none' | string(),
-
- %% Esp for TypEr
- %% ----------------------
- t_files = [] :: [string()],
-
- %% For choosing between contracts or comments
- contracts = true :: boolean(),
-
- %% Any file in 'final_files' is compilable.
- %% And we need to keep it as {FileName,ModuleName}
- %% in case filename does NOT match with moduleName
- final_files = [] :: [{string(), atom()}],
-
- ex_func = typer_map:new() :: dict(),
- record = typer_map:new() :: dict(),
-
- %% Functions: the line number of the function
- %% should be kept as well
- func = typer_map:new() :: dict(),
- inc_func = typer_map:new() :: dict(),
- trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}).
-
--record(args,
- {analyze = [] :: [string()],
- analyzed_dir_r = [] :: [string()],
- trust = [] :: [string()]}).
diff --git a/lib/typer/src/typer_annotator.erl b/lib/typer/src/typer_annotator.erl
deleted file mode 100644
index 68a8f03a5c..0000000000
--- a/lib/typer/src/typer_annotator.erl
+++ /dev/null
@@ -1,384 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%============================================================================
-%% File : typer_annotator.erl
-%% Author : Bingwen He <[email protected]>
-%% Description :
-%% If file 'FILENAME' has been analyzed, then the output of
-%% command "diff -B FILENAME.erl typer_ann/FILENAME.ann.erl"
-%% should be exactly what TypEr has added, namely type info.
-%%============================================================================
-
--module(typer_annotator).
-
--export([annotate/1]).
-
-%%----------------------------------------------------------------------------
-
--include("typer.hrl").
-
-%%----------------------------------------------------------------------------
-
--define(TYPER_ANN_DIR, "typer_ann").
-
--type func_info() :: {non_neg_integer(), atom(), arity()}.
-
--record(info, {recMap = typer_map:new() :: dict(),
- funcs = [] :: [func_info()],
- typeMap :: dict(),
- contracts :: boolean()}).
--record(inc, {map = typer_map:new() :: dict(),
- filter = [] :: [string()]}).
-
-%%----------------------------------------------------------------------------
-
--spec annotate(#typer_analysis{}) -> 'ok'.
-
-annotate(Analysis) ->
- case Analysis#typer_analysis.mode of
- ?SHOW -> show(Analysis);
- ?SHOW_EXPORTED -> show(Analysis);
- ?ANNOTATE ->
- Fun = fun({File, Module}) ->
- Info = get_final_info(File, Module, Analysis),
- write_typed_file(File, Info)
- end,
- lists:foreach(Fun, Analysis#typer_analysis.final_files);
- ?ANNOTATE_INC_FILES ->
- IncInfo = write_and_collect_inc_info(Analysis),
- write_inc_files(IncInfo)
- end.
-
-write_and_collect_inc_info(Analysis) ->
- Fun = fun({File, Module}, Inc) ->
- Info = get_final_info(File, Module, Analysis),
- write_typed_file(File, Info),
- IncFuns = get_functions(File, Analysis),
- collect_imported_funcs(IncFuns, Info#info.typeMap, Inc)
- end,
- NewInc = lists:foldl(Fun,#inc{}, Analysis#typer_analysis.final_files),
- clean_inc(NewInc).
-
-write_inc_files(Inc) ->
- Fun =
- fun (File) ->
- Val = typer_map:lookup(File,Inc#inc.map),
- %% Val is function with its type info
- %% in form [{{Line,F,A},Type}]
- Functions = [Key || {Key,_} <- Val],
- Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val],
- Info = #info{typeMap = typer_map:from_list(Val1),
- recMap = typer_map:new(),
- %% Note we need to sort functions here!
- funcs = lists:keysort(1, Functions)},
- %% io:format("TypeMap ~p\n", [Info#info.typeMap]),
- %% io:format("Funcs ~p\n", [Info#info.funcs]),
- %% io:format("RecMap ~p\n", [Info#info.recMap]),
- write_typed_file(File, Info)
- end,
- lists:foreach(Fun, dict:fetch_keys(Inc#inc.map)).
-
-show(Analysis) ->
- Fun = fun({File, Module}) ->
- Info = get_final_info(File, Module, Analysis),
- show_type_info_only(File, Info)
- end,
- lists:foreach(Fun, Analysis#typer_analysis.final_files).
-
-get_final_info(File, Module, Analysis) ->
- RecMap = get_recMap(File, Analysis),
- TypeMap = get_typeMap(Module, Analysis,RecMap),
- Functions = get_functions(File, Analysis),
- Contracts = Analysis#typer_analysis.contracts,
- #info{recMap=RecMap, funcs=Functions, typeMap=TypeMap, contracts=Contracts}.
-
-collect_imported_funcs(Funcs, TypeMap, TmpInc) ->
- %% Coming from other sourses, including:
- %% FIXME: How to deal with yecc-generated file????
- %% --.yrl (yecc-generated file)???
- %% -- yeccpre.hrl (yecc-generated file)???
- %% -- other cases
- Fun = fun({File,_} = Obj, Inc) ->
- case is_yecc_file(File, Inc) of
- {yecc_generated, NewInc} -> NewInc;
- {not_yecc, NewInc} ->
- check_imported_funcs(Obj, NewInc, TypeMap)
- end
- end,
- lists:foldl(Fun, TmpInc, Funcs).
-
--spec is_yecc_file(string(), #inc{}) -> {'not_yecc', #inc{}}
- | {'yecc_generated', #inc{}}.
-is_yecc_file(File, Inc) ->
- case lists:member(File, Inc#inc.filter) of
- true -> {yecc_generated, Inc};
- false ->
- case filename:extension(File) of
- ".yrl" ->
- Rootname = filename:rootname(File, ".yrl"),
- Obj = Rootname ++ ".erl",
- case lists:member(Obj, Inc#inc.filter) of
- true -> {yecc_generated, Inc};
- false ->
- NewFilter = [Obj|Inc#inc.filter],
- NewInc = Inc#inc{filter = NewFilter},
- {yecc_generated, NewInc}
- end;
- _ ->
- case filename:basename(File) of
- "yeccpre.hrl" -> {yecc_generated, Inc};
- _ -> {not_yecc, Inc}
- end
- end
- end.
-
-check_imported_funcs({File, {Line, F, A}}, Inc, TypeMap) ->
- IncMap = Inc#inc.map,
- FA = {F, A},
- Type = get_type_info(FA, TypeMap),
- case typer_map:lookup(File, IncMap) of
- none -> %% File is not added. Add it
- Obj = {File,[{FA, {Line, Type}}]},
- NewMap = typer_map:insert(Obj, IncMap),
- Inc#inc{map = NewMap};
- Val -> %% File is already in. Check.
- case lists:keyfind(FA, 1, Val) of
- false ->
- %% Function is not in; add it
- Obj = {File, Val ++ [{FA, {Line, Type}}]},
- NewMap = typer_map:insert(Obj, IncMap),
- Inc#inc{map = NewMap};
- Type ->
- %% Function is in and with same type
- Inc;
- _ ->
- %% Function is in but with diff type
- inc_warning(FA, File),
- Elem = lists:keydelete(FA, 1, Val),
- NewMap = case Elem of
- [] ->
- typer_map:remove(File, IncMap);
- _ ->
- typer_map:insert({File, Elem}, IncMap)
- end,
- Inc#inc{map = NewMap}
- end
- end.
-
-inc_warning({F, A}, File) ->
- io:format(" ***Warning: Skip function ~p/~p ", [F, A]),
- io:format("in file ~p because of inconsistent type\n", [File]).
-
-clean_inc(Inc) ->
- Inc1 = remove_yecc_generated_file(Inc),
- normalize_obj(Inc1).
-
-remove_yecc_generated_file(TmpInc) ->
- Fun = fun(Key, Inc) ->
- NewMap = typer_map:remove(Key, Inc#inc.map),
- Inc#inc{map = NewMap}
- end,
- lists:foldl(Fun, TmpInc, TmpInc#inc.filter).
-
-normalize_obj(TmpInc) ->
- Fun = fun(Key, Val, Inc) ->
- NewVal = [{{Line,F,A},Type} || {{F,A},{Line,Type}} <- Val],
- typer_map:insert({Key,NewVal}, Inc)
- end,
- NewMap = typer_map:fold(Fun, typer_map:new(), TmpInc#inc.map),
- TmpInc#inc{map = NewMap}.
-
-get_recMap(File, Analysis) ->
- typer_map:lookup(File, Analysis#typer_analysis.record).
-
-get_typeMap(Module, Analysis, RecMap) ->
- TypeInfoPlt = Analysis#typer_analysis.trust_plt,
- TypeInfo =
- case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of
- none -> [];
- {value, List} -> List
- end,
- CodeServer = Analysis#typer_analysis.code_server,
- TypeInfoList = [get_type(I, CodeServer, RecMap) || I <- TypeInfo],
- typer_map:from_list(TypeInfoList).
-
-get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, RecMap) ->
- case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of
- error ->
- {{F, A}, {Range, Arg}};
- {ok, {_FileLine, Contract}} ->
- Sig = erl_types:t_fun(Arg, Range),
- case dialyzer_contracts:check_contract(Contract, Sig) of
- ok -> {{F, A}, {contract, Contract}};
- {error, {extra_range, _, _}} ->
- {{F, A}, {contract, Contract}};
- {error, invalid_contract} ->
- CString = dialyzer_contracts:contract_to_string(Contract),
- SigString = dialyzer_utils:format_sig(Sig, RecMap),
- typer:error(
- io_lib:format("Error in contract of function ~w:~w/~w\n"
- "\t The contract is: " ++ CString ++ "\n" ++
- "\t but the inferred signature is: ~s",
- [M, F, A, SigString]));
- {error, Msg} when is_list(Msg) -> % Msg is a string()
- typer:error(
- io_lib:format("Error in contract of function ~w:~w/~w: ~s",
- [M, F, A, Msg]))
- end
- end.
-
-get_functions(File, Analysis) ->
- case Analysis#typer_analysis.mode of
- ?SHOW ->
- Funcs = typer_map:lookup(File, Analysis#typer_analysis.func),
- Inc_Funcs = typer_map:lookup(File, Analysis#typer_analysis.inc_func),
- remove_module_info(Funcs) ++ normalize_incFuncs(Inc_Funcs);
- ?SHOW_EXPORTED ->
- Ex_Funcs = typer_map:lookup(File, Analysis#typer_analysis.ex_func),
- remove_module_info(Ex_Funcs);
- ?ANNOTATE ->
- Funcs = typer_map:lookup(File, Analysis#typer_analysis.func),
- remove_module_info(Funcs);
- ?ANNOTATE_INC_FILES ->
- typer_map:lookup(File, Analysis#typer_analysis.inc_func)
- end.
-
-normalize_incFuncs(Funcs) ->
- [FuncInfo || {_FileName, FuncInfo} <- Funcs].
-
--spec remove_module_info([func_info()]) -> [func_info()].
-
-remove_module_info(FuncInfoList) ->
- F = fun ({_,module_info,0}) -> false;
- ({_,module_info,1}) -> false;
- ({Line,F,A}) when is_integer(Line), is_atom(F), is_integer(A) -> true
- end,
- lists:filter(F, FuncInfoList).
-
-write_typed_file(File, Info) ->
- io:format(" Processing file: ~p\n", [File]),
- Dir = filename:dirname(File),
- RootName = filename:basename(filename:rootname(File)),
- Ext = filename:extension(File),
- TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR),
- TmpNewFilename = lists:concat([RootName,".ann",Ext]),
- NewFileName = filename:join(TyperAnnDir, TmpNewFilename),
- case file:make_dir(TyperAnnDir) of
- {error, Reason} ->
- case Reason of
- eexist -> %% TypEr dir exists; remove old typer files
- ok = file:delete(NewFileName),
- write_typed_file(File, Info, NewFileName);
- enospc ->
- io:format(" Not enough space in ~p\n", [Dir]);
- eacces ->
- io:format(" No write permission in ~p\n", [Dir]);
- _ ->
- io:format("Unknown error when writing ~p\n", [Dir]),
- halt()
- end;
- ok -> %% Typer dir does NOT exist
- write_typed_file(File, Info, NewFileName)
- end.
-
-write_typed_file(File, Info, NewFileName) ->
- {ok, Binary} = file:read_file(File),
- Chars = binary_to_list(Binary),
- write_typed_file(Chars, NewFileName, Info, 1, []),
- io:format(" Saved as: ~p\n", [NewFileName]).
-
-write_typed_file(Chars, File, #info{funcs = []}, _LNo, _Acc) ->
- ok = file:write_file(File, list_to_binary(Chars), [append]);
-write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) ->
- [{Line,F,A}|RestFuncs] = Info#info.funcs,
- case Line of
- 1 -> %% This will happen only for inc files
- ok = raw_write(F, A, Info, File, []),
- NewInfo = Info#info{funcs = RestFuncs},
- NewAcc = [],
- write_typed_file(Chars, File, NewInfo, Line, NewAcc);
- _ ->
- case Ch of
- 10 ->
- NewLineNo = LineNo + 1,
- {NewInfo, NewAcc} =
- case NewLineNo of
- Line ->
- ok = raw_write(F, A, Info, File, [Ch|Acc]),
- {Info#info{funcs = RestFuncs}, []};
- _ ->
- {Info, [Ch|Acc]}
- end,
- write_typed_file(Chs, File, NewInfo, NewLineNo, NewAcc);
- _ ->
- write_typed_file(Chs, File, Info, LineNo, [Ch|Acc])
- end
- end.
-
-raw_write(F, A, Info, File, Content) ->
- TypeInfo = get_type_string(F, A, Info, file),
- ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n",
- ContentBin = list_to_binary(ContentList),
- file:write_file(File, ContentBin, [append]).
-
-get_type_string(F, A, Info, Mode) ->
- Type = get_type_info({F,A}, Info#info.typeMap),
- TypeStr =
- case Type of
- {contract, C} ->
- dialyzer_contracts:contract_to_string(C);
- {RetType, ArgType} ->
- dialyzer_utils:format_sig(erl_types:t_fun(ArgType, RetType),
- Info#info.recMap)
- end,
- case Info#info.contracts of
- true ->
- case {Mode, Type} of
- {file, {contract, _}} -> "";
- _ ->
- Prefix = lists:concat(["-spec ", F]),
- lists:concat([Prefix, TypeStr, "."])
- end;
- false ->
- Prefix = lists:concat(["%% @spec ", F]),
- lists:concat([Prefix, TypeStr, "."])
- end.
-
-show_type_info_only(File, Info) ->
- io:format("\n%% File: ~p\n%% ", [File]),
- OutputString = lists:concat(["~.", length(File)+8, "c~n"]),
- io:fwrite(OutputString, [$-]),
- Fun = fun ({_LineNo, F, A}) ->
- TypeInfo = get_type_string(F, A, Info, show),
- io:format("~s\n", [TypeInfo])
- end,
- lists:foreach(Fun, Info#info.funcs).
-
-get_type_info(Func, TypeMap) ->
- case typer_map:lookup(Func, TypeMap) of
- none ->
- %% Note: Typeinfo of any function should exist in
- %% the result offered by dialyzer, otherwise there
- %% *must* be something wrong with the analysis
- io:format("No type info for function: ~p\n", [Func]),
- halt();
- {contract, _Fun} = C -> C;
- {_RetType, _ArgType} = RA -> RA
- end.
diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl
deleted file mode 100644
index ea25fa6f68..0000000000
--- a/lib/typer/src/typer_info.erl
+++ /dev/null
@@ -1,162 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(typer_info).
-
--export([collect/1]).
-
--type func_info() :: {non_neg_integer(), atom(), arity()}.
--type inc_file_info() :: {string(), func_info()}.
-
--record(tmpAcc, {file :: string(),
- module :: atom(),
- funcAcc=[] :: [func_info()],
- incFuncAcc=[] :: [inc_file_info()],
- dialyzerObj=[] :: [{mfa(), {_, _}}]}).
-
--include("typer.hrl").
-
--spec collect(#typer_analysis{}) -> #typer_analysis{}.
-
-collect(Analysis) ->
- NewPlt =
- try get_dialyzer_plt(Analysis) of
- DialyzerPlt ->
- dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt])
- catch
- throw:{dialyzer_error,_Reason} ->
- typer:error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
- end,
- NewAnalysis = lists:foldl(fun collect_one_file_info/2,
- Analysis#typer_analysis{trust_plt = NewPlt},
- Analysis#typer_analysis.ana_files),
- %% Process Remote Types
- TmpCServer = NewAnalysis#typer_analysis.code_server,
- NewCServer =
- try
- NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
- OldRecords = dialyzer_plt:get_types(NewPlt),
- MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
- %% io:format("Merged Records ~p",[MergedRecords]),
- TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
- TmpCServer2 = dialyzer_utils:process_record_remote_types(TmpCServer1),
- dialyzer_contracts:process_contract_remote_types(TmpCServer2)
- catch
- throw:{error, ErrorMsg} ->
- typer:error(ErrorMsg)
- end,
- NewAnalysis#typer_analysis{code_server = NewCServer}.
-
-collect_one_file_info(File, Analysis) ->
- Ds = [{d,Name,Val} || {Name,Val} <- Analysis#typer_analysis.macros],
- %% Current directory should also be included in "Includes".
- Includes = [filename:dirname(File)|Analysis#typer_analysis.includes],
- Is = [{i,Dir} || Dir <- Includes],
- Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
- case dialyzer_utils:get_abstract_code_from_src(File, Options) of
- {error, Reason} ->
- %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
- typer:compile_error(Reason);
- {ok, AbstractCode} ->
- case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
- error -> typer:compile_error(["Could not get core erlang for "++File]);
- {ok, Core} ->
- case dialyzer_utils:get_record_and_type_info(AbstractCode) of
- {error, Reason} -> typer:compile_error([Reason]);
- {ok, Records} ->
- Mod = list_to_atom(filename:basename(File, ".erl")),
- case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
- {error, Reason} -> typer:compile_error([Reason]);
- {ok, SpecInfo} ->
- analyze_core_tree(Core, Records, SpecInfo, Analysis, File)
- end
- end
- end
- end.
-
-analyze_core_tree(Core, Records, SpecInfo, Analysis, File) ->
- Module = list_to_atom(filename:basename(File, ".erl")),
- TmpTree = cerl:from_records(Core),
- CS1 = Analysis#typer_analysis.code_server,
- NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
- {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
- CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
- CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
- CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
- CS5 = dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4),
- Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
- TmpCG = Analysis#typer_analysis.callgraph,
- CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
- Fun = fun analyze_one_function/2,
- All_Defs = cerl:module_defs(Tree),
- Acc = lists:foldl(Fun, #tmpAcc{file=File, module=Module}, All_Defs),
- Exported_FuncMap = typer_map:insert({File, Ex_Funcs},
- Analysis#typer_analysis.ex_func),
- %% NOTE: we must sort all functions in the file which
- %% originate from this file by *numerical order* of lineNo
- Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
- FuncMap = typer_map:insert({File, Sorted_Functions},
- Analysis#typer_analysis.func),
- %% NOTE: However we do not need to sort functions
- %% which are imported from included files.
- IncFuncMap = typer_map:insert({File, Acc#tmpAcc.incFuncAcc},
- Analysis#typer_analysis.inc_func),
- Final_Files = Analysis#typer_analysis.final_files ++ [{File, Module}],
- RecordMap = typer_map:insert({File, Records}, Analysis#typer_analysis.record),
- Analysis#typer_analysis{final_files=Final_Files,
- callgraph=CG,
- code_server=CS5,
- ex_func=Exported_FuncMap,
- inc_func=IncFuncMap,
- record=RecordMap,
- func=FuncMap}.
-
-analyze_one_function({Var, FunBody} = Function, Acc) ->
- F = cerl:fname_id(Var),
- A = cerl:fname_arity(Var),
- TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
- NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
- [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
- BaseName = filename:basename(FileName),
- FuncInfo = {LineNo, F, A},
- OriginalName = Acc#tmpAcc.file,
- {FuncAcc, IncFuncAcc} =
- case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
- true -> %% Coming from original file
- %% io:format("Added function ~p\n", [{LineNo, F, A}]),
- {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
- false ->
- %% Coming from other sourses, including:
- %% -- .yrl (yecc-generated file)
- %% -- yeccpre.hrl (yecc-generated file)
- %% -- other cases
- {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
- end,
- Acc#tmpAcc{funcAcc = FuncAcc,
- incFuncAcc = IncFuncAcc,
- dialyzerObj = NewDialyzerObj}.
-
-get_dialyzer_plt(#typer_analysis{plt = PltFile0}) ->
- PltFile =
- case PltFile0 =:= none of
- true -> dialyzer_plt:get_default_plt();
- false -> PltFile0
- end,
- dialyzer_plt:from_file(PltFile).
diff --git a/lib/typer/src/typer_map.erl b/lib/typer/src/typer_map.erl
deleted file mode 100644
index bf62dea651..0000000000
--- a/lib/typer/src/typer_map.erl
+++ /dev/null
@@ -1,47 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(typer_map).
-
--export([new/0, insert/2, lookup/2, from_list/1, remove/2, fold/3]).
-
--spec new() -> dict().
-new() ->
- dict:new().
-
--spec insert({term(), term()}, dict()) -> dict().
-insert(Object, Dict) ->
- {Key, Value} = Object,
- dict:store(Key, Value, Dict).
-
--spec lookup(term(), dict()) -> any().
-lookup(Key, Dict) ->
- try dict:fetch(Key, Dict) catch error:_ -> none end.
-
--spec from_list([{term(), term()}]) -> dict().
-from_list(List) ->
- dict:from_list(List).
-
--spec remove(term(), dict()) -> dict().
-remove(Key, Dict) ->
- dict:erase(Key, Dict).
-
--spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term().
-fold(Fun, Acc0, Dict) ->
- dict:fold(Fun, Acc0, Dict).
diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl
deleted file mode 100644
index 1e53b1b305..0000000000
--- a/lib/typer/src/typer_options.erl
+++ /dev/null
@@ -1,191 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%===========================================================================
-%% File : typer_options.erl
-%% Author : Bingwen He <[email protected]>
-%% Description : Handles all command-line options given to TypEr
-%%===========================================================================
-
--module(typer_options).
-
--export([process/0]).
-
-%%---------------------------------------------------------------------------
-
--include("typer.hrl").
-
-%%---------------------------------------------------------------------------
-%% Exported functions
-%%---------------------------------------------------------------------------
-
--spec process() -> {#args{}, #typer_analysis{}}.
-
-process() ->
- ArgList = init:get_plain_arguments(),
- %% io:format("Args is ~p\n",[Args]),
- {Args, Analysis} = analyze_args(ArgList, #args{}, #typer_analysis{}),
- %% if the mode has not been set, set it to the default mode (show)
- {Args, case Analysis#typer_analysis.mode of
- undefined -> Analysis#typer_analysis{mode = ?SHOW};
- Mode when is_atom(Mode) -> Analysis
- end}.
-
-%%---------------------------------------------------------------------------
-%% Internal functions
-%%---------------------------------------------------------------------------
-
-analyze_args([], Args, Analysis) ->
- {Args, Analysis};
-analyze_args(ArgList, Args, Analysis) ->
- {Result, Rest} = cl(ArgList),
- {NewArgs, NewAnalysis} = analyze_result(Result, Args, Analysis),
- analyze_args(Rest, NewArgs, NewAnalysis).
-
-cl(["-h"|_]) -> help_message();
-cl(["--help"|_]) -> help_message();
-cl(["-v"|_]) -> version_message();
-cl(["--version"|_]) -> version_message();
-cl(["--comments"|Opts]) -> {comments, Opts};
-cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts};
-cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
-cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
-cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts};
-cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts};
-cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts};
-cl(["-D"++Def|Opts]) ->
- case Def of
- "" -> typer:error("no variable name specified after -D");
- _ ->
- L = re:split(Def, "=", [{return, list}]),
- DefPair = process_def_list(L),
- {{def, DefPair}, Opts}
- end;
-cl(["-I",Dir|Opts]) -> {{inc,Dir}, Opts};
-cl(["-I"++Dir|Opts]) ->
- case Dir of
- "" -> typer:error("no include directory specified after -I");
- _ -> {{inc, Dir}, Opts}
- end;
-cl(["-T"|Opts]) ->
- {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
- case Files of
- [] -> typer:error("no file or directory specified after -T");
- [_|_] -> {{trust, Files}, RestOpts}
- end;
-cl(["-r"|Opts]) ->
- {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
- {{a_dir_r, Files}, RestOpts};
-cl(["-"++H|_]) -> typer:error("unknown option -"++H);
-cl(Opts) ->
- {Args, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
- {{analyze, Args}, RestOpts}.
-
-process_def_list(L) ->
- case L of
- [Name, Value] ->
- {ok, Tokens, _} = erl_scan:string(Value ++ "."),
- {ok, ErlValue} = erl_parse:parse_term(Tokens),
- {list_to_atom(Name), ErlValue};
- [Name] ->
- {list_to_atom(Name), true}
- end.
-
-%% Get information about files that the user trusts and wants to analyze
-analyze_result({analyze, Val}, Args, Analysis) ->
- NewVal = Args#args.analyze ++ Val,
- {Args#args{analyze = NewVal}, Analysis};
-analyze_result({a_dir_r, Val}, Args, Analysis) ->
- NewVal = Args#args.analyzed_dir_r ++ Val,
- {Args#args{analyzed_dir_r = NewVal}, Analysis};
-analyze_result({trust, Val}, Args, Analysis) ->
- NewVal = Args#args.trust ++ Val,
- {Args#args{trust = NewVal}, Analysis};
-analyze_result(comments, Args, Analysis) ->
- {Args, Analysis#typer_analysis{contracts = false}};
-%% Get useful information for actual analysis
-analyze_result({mode, Val}, Args, Analysis) ->
- case Analysis#typer_analysis.mode of
- undefined -> {Args, Analysis#typer_analysis{mode = Val}};
- _ -> mode_error()
- end;
-analyze_result({def, Val}, Args, Analysis) ->
- NewVal = Analysis#typer_analysis.macros ++ [Val],
- {Args, Analysis#typer_analysis{macros = NewVal}};
-analyze_result({inc, Val}, Args, Analysis) ->
- NewVal = Analysis#typer_analysis.includes ++ [Val],
- {Args, Analysis#typer_analysis{includes = NewVal}};
-analyze_result({plt, Plt}, Args, Analysis) ->
- {Args, Analysis#typer_analysis{plt = Plt}}.
-
-%%--------------------------------------------------------------------
-
--spec mode_error() -> no_return().
-mode_error() ->
- typer:error("can not do \"show\", \"show-exported\", \"annotate\", and \"annotate-inc-files\" at the same time").
-
--spec version_message() -> no_return().
-version_message() ->
- io:format("TypEr version "++?VSN++"\n"),
- erlang:halt(0).
-
--spec help_message() -> no_return().
-help_message() ->
- S = " Usage: typer [--help] [--version] [--comments] [--plt PLT]
- [--show | --show-exported | --annotate | --annotate-inc-files]
- [-Ddefine]* [-I include_dir]* [-T application]* [-r] file*
-
- Options:
- -r dir*
- search directories recursively for .erl files below them
- --show
- Prints type specifications for all functions on stdout.
- (this is the default behaviour; this option is not really needed)
- --show-exported (or --show_exported)
- Same as --show, but prints specifications for exported functions only
- Specs are displayed sorted alphabetically on the function's name
- --annotate
- Annotates the specified files with type specifications
- --annotate-inc-files
- Same as --annotate but annotates all -include() files as well as
- all .erl files (use this option with caution - has not been tested much)
- --comments
- Prints type information using Edoc comments, not type specs
- --plt PLT
- Use the specified dialyzer PLT file rather than the default one
- -T file*
- The specified file(s) already contain type specifications and these
- are to be trusted in order to print specs for the rest of the files
- (Multiple files or dirs, separated by spaces, can be specified.)
- -Dname (or -Dname=value)
- pass the defined name(s) to TypEr
- (The syntax of defines is the same as that used by \"erlc\".)
- -I include_dir
- pass the include_dir to TypEr
- (The syntax of includes is the same as that used by \"erlc\".)
- --version (or -v)
- prints the Typer version and exits
- --help (or -h)
- prints this message and exits
-
- Note:
- * denotes that multiple occurrences of these options are possible.
-",
- io:put_chars(S),
- erlang:halt(0).
diff --git a/lib/typer/src/typer_preprocess.erl b/lib/typer/src/typer_preprocess.erl
deleted file mode 100644
index 7cb0b9932b..0000000000
--- a/lib/typer/src/typer_preprocess.erl
+++ /dev/null
@@ -1,154 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(typer_preprocess).
-
--export([get_all_files/2]).
-
--include("typer.hrl").
-
-%%----------------------------------------------------------------------------
-
--spec get_all_files(#args{}, 'analysis' | 'trust') -> [string()].
-
-get_all_files(Args, analysis) ->
- case internal_get_all_files(Args#args.analyze,
- Args#args.analyzed_dir_r,
- fun test_erl_file_exclude_ann/1) of
- [] -> typer:error("no file(s) to analyze");
- AllFiles -> AllFiles
- end;
-get_all_files(Args, trust) ->
- internal_get_all_files(Args#args.trust, [], fun test_erl_file/1).
-
--spec test_erl_file_exclude_ann(string()) -> boolean().
-
-test_erl_file_exclude_ann(File) ->
- case filename:extension(File) of
- ".erl" -> %% Exclude files ending with ".ann.erl"
- case re:run(File, "[\.]ann[\.]erl$") of
- {match, _} -> false;
- nomatch -> true
- end;
- _ -> false
- end.
-
--spec test_erl_file(string()) -> boolean().
-
-test_erl_file(File) ->
- filename:extension(File) =:= ".erl".
-
--spec internal_get_all_files([string()], [string()],
- fun((string()) -> boolean())) -> [string()].
-
-internal_get_all_files(File_Dir, Dir_R, Fun) ->
- All_File_1 = process_file_and_dir(File_Dir, Fun),
- All_File_2 = process_dir_recursively(Dir_R, Fun),
- remove_dup(All_File_1 ++ All_File_2).
-
--spec process_file_and_dir([string()],
- fun((string()) -> boolean())) -> [string()].
-
-process_file_and_dir(File_Dir, TestFun) ->
- Fun =
- fun (Elem, Acc) ->
- case filelib:is_regular(Elem) of
- true -> process_file(Elem, TestFun, Acc);
- false -> check_dir(Elem, non_recursive, Acc, TestFun)
- end
- end,
- lists:foldl(Fun, [], File_Dir).
-
--spec process_dir_recursively([string()],
- fun((string()) -> boolean())) -> [string()].
-
-process_dir_recursively(Dirs, TestFun) ->
- Fun = fun (Dir, Acc) ->
- check_dir(Dir, recursive, Acc, TestFun)
- end,
- lists:foldl(Fun, [], Dirs).
-
--spec check_dir(string(),
- 'non_recursive' | 'recursive',
- [string()],
- fun((string()) -> boolean())) -> [string()].
-
-check_dir(Dir, Mode, Acc, Fun) ->
- case file:list_dir(Dir) of
- {ok, Files} ->
- {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir),
- case Mode of
- non_recursive ->
- FinalFiles = process_file_and_dir(TmpFiles, Fun),
- Acc ++ FinalFiles;
- recursive ->
- TmpAcc1 = process_file_and_dir(TmpFiles, Fun),
- TmpAcc2 = process_dir_recursively(TmpDirs, Fun),
- Acc ++ TmpAcc1 ++ TmpAcc2
- end;
- {error, eacces} ->
- typer:error("no access permission to dir \""++Dir++"\"");
- {error, enoent} ->
- typer:error("cannot access "++Dir++": No such file or directory");
- {error, _Reason} ->
- typer:error("error involving a use of file:list_dir/1")
- end.
-
-%% Same order as the input list
--spec process_file(string(), fun((string()) -> boolean()), string()) -> [string()].
-
-process_file(File, TestFun, Acc) ->
- case TestFun(File) of
- true -> Acc ++ [File];
- false -> Acc
- end.
-
-%% Same order as the input list
--spec split_dirs_and_files([string()], string()) -> {[string()], [string()]}.
-
-split_dirs_and_files(Elems, Dir) ->
- Test_Fun =
- fun (Elem, {DirAcc, FileAcc}) ->
- File = filename:join(Dir, Elem),
- case filelib:is_regular(File) of
- false -> {[File|DirAcc], FileAcc};
- true -> {DirAcc, [File|FileAcc]}
- end
- end,
- {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems),
- {lists:reverse(Dirs), lists:reverse(Files)}.
-
-%%-----------------------------------------------------------------------
-%% Utilities
-%%-----------------------------------------------------------------------
-
-%% Removes duplicate filenames but it keeps the order of the input list
-
--spec remove_dup([string()]) -> [string()].
-
-remove_dup(Files) ->
- Test_Dup = fun (File, Acc) ->
- case lists:member(File, Acc) of
- true -> Acc;
- false -> [File|Acc]
- end
- end,
- Reversed_Elems = lists:foldl(Test_Dup, [], Files),
- lists:reverse(Reversed_Elems).
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index 7f4aabb335..51561939ac 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.1.7.5
+TYPER_VSN = 0.9