浏览代码

ft: 代码修改

master
SisMaker 3 年前
父节点
当前提交
deafa022b6
共有 8 个文件被更改,包括 170 次插入299 次删除
  1. +1
    -1
      README.md
  2. +0
    -257
      c_src/tracer/erl_tracer_nif.c1
  3. +6
    -4
      include/eTpf.hrl
  4. +1
    -0
      rebar.config
  5. +47
    -33
      src/tracer/tpTracerFile.erl
  6. +112
    -0
      src/tracer/tpTracerLog.erl
  7. +2
    -3
      src/utils/tpTermCut.erl
  8. +1
    -1
      test/lg_SUITE.erl

+ 1
- 1
README.md 查看文件

@ -236,7 +236,7 @@ end.
对于长时间运行的会话,eTpf可以旋转跟踪文件。这是一项有助于避免磁盘空间用完的功能,并且不用于保留较小的文件(eTpf可以处理非常大的文件就可以了)。
eTpf:trace/3,4可以提供一个映射,而不是将文件名前缀作为第三个参数传递 给。当前有三个选项,包括filename_prefix。其他选项是最大文件大小(以字节为单位)fMaxSize,以及将在文件中每LZ4帧存储的事件数
fEventFrame。这两个选项使您可以控制文件写入或旋转的频率。
fMaxLog。这两个选项使您可以控制文件写入或旋转的频率。
以下示例将文件大小限制为100MB:

+ 0
- 257
c_src/tracer/erl_tracer_nif.c1 查看文件

@ -1,257 +0,0 @@
/*
* %CopyrightBegin%
*
* Copyright Ericsson 2015-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
/*
* Purpose: NIF library for process/port tracer
*
*/
#define STATIC_ERLANG_NIF 1
#include "erl_nif.h"
#include "config.h"
#include "sys.h"
#ifdef VALGRIND
# include <valgrind/memcheck.h>
#endif
/* NIF interface declarations */
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
static void unload(ErlNifEnv* env, void* priv_data);
/* The NIFs: */
static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
{"enabled", 3, enabled},
{"trace", 5, trace}
};
ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload)
#define ATOMS \
ATOM_DECL(call); \
ATOM_DECL(command); \
ATOM_DECL(cpu_timestamp); \
ATOM_DECL(discard); \
ATOM_DECL(exception_from); \
ATOM_DECL(extra); \
ATOM_DECL(match_spec_result); \
ATOM_DECL(monotonic); \
ATOM_DECL(ok); \
ATOM_DECL(remove); \
ATOM_DECL(return_from); \
ATOM_DECL(scheduler_id); \
ATOM_DECL(send); \
ATOM_DECL(send_to_non_existing_process); \
ATOM_DECL(seq_trace); \
ATOM_DECL(spawn); \
ATOM_DECL(strict_monotonic); \
ATOM_DECL(timestamp); \
ATOM_DECL(trace); \
ATOM_DECL(trace_status); \
ATOM_DECL(trace_ts); \
ATOM_DECL(true); \
ATOM_DECL(gc_minor_start); \
ATOM_DECL(gc_minor_end); \
ATOM_DECL(gc_major_start); \
ATOM_DECL(gc_major_end);
#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
ATOMS
#undef ATOM_DECL
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
#define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A)
ATOMS
#undef ATOM_DECL
*priv_data = NULL;
return 0;
}
static void unload(ErlNifEnv* env, void* priv_data)
{
}
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
ERL_NIF_TERM load_info)
{
if (*old_priv_data != NULL) {
return -1; /* Don't know how to do that */
}
if (*priv_data != NULL) {
return -1; /* Don't know how to do that */
}
if (load(env, priv_data, load_info)) {
return -1;
}
return 0;
}
static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPid to_pid;
ErlNifPort to_port;
ERL_NIF_TERM ret = enif_is_identical(argv[0], atom_trace_status) ?
atom_remove : atom_discard;
ASSERT(argc == 3);
if (enif_get_local_pid(env, argv[1], &to_pid)) {
if (!enif_is_process_alive(env, &to_pid))
/* tracer is dead so we should remove this trace point */
return ret;
} else if (enif_get_local_port(env, argv[1], &to_port)) {
if (!enif_is_port_alive(env, &to_port))
/* tracer is dead so we should remove this trace point */
return ret;
} else {
/* The state was not a pid or a port */
return ret;
}
/* Only generate trace for when tracer != tracee */
if (enif_is_identical(argv[1], argv[2])) {
return atom_discard;
}
return atom_trace;
}
/*
-spec trace(seq_trace, TracerState :: pid() | port(),
Label :: non_neg_integer(),
Msg :: term(),
Opts :: map()) -> ignored();
trace(Tag :: atom(), TracerState :: pid() | port(),
Tracee :: pid() || port() || undefined,
Msg :: term(),
Opts :: map()) -> ignored().
*/
static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM value, msg, tt[8], opts;
ErlNifPid to_pid;
ErlNifPort to_port;
size_t tt_sz = 0;
int is_port = 0;
size_t opts_sz = 0;
ASSERT(argc == 5);
if (!enif_get_local_pid(env, argv[1], &to_pid)) {
if (!enif_get_local_port(env, argv[1], &to_port)) {
/* This only fails if argv[1] is a not a local port/pid
which should not happen as it is checked in enabled */
ASSERT(0);
return atom_ok;
}
is_port = 1;
}
opts = argv[4];
if (!enif_get_map_size(env, opts, &opts_sz))
opts_sz = 0;
if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) {
tt[tt_sz++] = atom_trace;
tt[tt_sz++] = argv[2];
tt[tt_sz++] = argv[0];
tt[tt_sz++] = argv[3];
tt[tt_sz++] = value;
} else {
if (enif_is_identical(argv[0], atom_seq_trace)) {
tt[tt_sz++] = atom_seq_trace;
tt[tt_sz++] = argv[2];
tt[tt_sz++] = argv[3];
} else {
tt[tt_sz++] = atom_trace;
tt[tt_sz++] = argv[2];
tt[tt_sz++] = argv[0];
tt[tt_sz++] = argv[3];
}
}
if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) {
tt[tt_sz++] = value;
}
if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) {
tt[tt_sz++] = value;
}
if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) {
ERL_NIF_TERM ts;
if (enif_is_identical(value, atom_monotonic)) {
ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
ts = enif_make_int64(env, mon);
} else if (enif_is_identical(value, atom_strict_monotonic)) {
ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
ERL_NIF_TERM unique = enif_make_unique_integer(
env, ERL_NIF_UNIQUE_MONOTONIC);
ts = enif_make_tuple2(env, enif_make_int64(env, mon), unique);
} else if (enif_is_identical(value, atom_timestamp)) {
ts = enif_now_time(env);
} else if (enif_is_identical(value, atom_cpu_timestamp)) {
ts = enif_cpu_time(env);
} else {
ASSERT(0);
goto error;
}
tt[tt_sz++] = ts;
if (tt[0] == atom_trace)
tt[0] = atom_trace_ts;
}
msg = enif_make_tuple_from_array(env, tt, tt_sz);
if (is_port) {
ErlNifBinary bin;
if (!enif_term_to_binary(env, msg, &bin))
goto error;
msg = enif_make_binary(env, &bin);
(void) enif_port_command(env, &to_port, NULL, msg);
/* if failure: port has probably died, enabled will clean up */
enif_release_binary(&bin);
} else {
(void) enif_send(env, &to_pid, NULL, msg);
/* if failure: process has probably died, enabled will clean up */
}
error:
return atom_ok;
}

+ 6
- 4
include/eTpf.hrl 查看文件

@ -38,14 +38,16 @@
port => pos_integer()
%% tracer for file
, fBaseName => string()
, fMaxSize => pos_integer()
, fEventFrame => pos_integer()
, fDir => string() %%
, fBaseName => string() %% base file name
, fMaxSize => pos_integer() %%
, fMaxLog => pos_integer() %%
, fMaxMsg => pos_integer() %%
%% tracer for console
%% trace msg term cut cfg
, tcmIsCut => boolean() %% cut trace msg term
, tcmIsCut => boolean() %% cut trace msg term
, tcmDepth => pos_integer() %% term cut时保留的最大深度
, tcmListSize => pos_integer() %% List term cut时保留的最大数量
, tcmMapSize => pos_integer() %% Map term cut时保留的最大数量

+ 1
- 0
rebar.config 查看文件

@ -1,6 +1,7 @@
{erl_opts, [debug_info]}.
{deps, [
{eFmt, ".*", {git, "http://192.168.0.88:53000/SisMaker/eFmt.git", {branch, "master"}}},
{eSync, ".*", {git, "http://192.168.0.88:53000/SisMaker/eSync.git", {branch, "master"}}},
{eGbh, ".*", {git, "http://192.168.0.88:53000/SisMaker/eGbh.git", {branch, "master"}}}
]}.

+ 47
- 33
src/tracer/tpTracerFile.erl 查看文件

@ -15,15 +15,15 @@
-record(state, {
parent :: pid()
, fBaseName :: file:filename_all()
, fDir :: file:filename_all() %% file base name
, fBaseName :: file:filename_all() %% file base name
, size = 0 :: non_neg_integer()
, fMaxSize :: infinity | non_neg_integer()
, ioDevice :: file:io_device()
, fEventFrame :: pos_integer()
, eventsThisFrame = 0 :: non_neg_integer()
, buffer = <<>> :: binary()
, tcmIsCut = false
, fMaxMsg = 0 :: non_neg_integer()
, msgCnt = 0
, msgList = []
}).
start_link(Opts) ->
@ -34,7 +34,9 @@ init(Parent, TracerOpts) ->
process_flag(message_queue_data, off_heap),
process_flag(trap_exit, true),
%% No need to close the file, it'll be closed when the process exits.
Filename = maps:get(fBaseName, TracerOpts, "traces.lz4"),
FDir = maps:get(fDir, TracerOpts, <<"./">>),
FBaseName = maps:get(fBaseName, TracerOpts, <<"traces.zip">>),
Filename = fileName(FDir, FBaseName),
{ok, IoDevice} = file:open(Filename, [write, raw]),
TcmIsCut = maps:get(tcmIsCut, TracerOpts, maps:get(tcmDepth, ?defTcmMap)),
@ -53,15 +55,16 @@ init(Parent, TracerOpts) ->
State = #state{
parent = Parent
, fBaseName = Filename
, fDir = FDir
, fBaseName = FBaseName
, ioDevice = IoDevice
, fMaxSize = maps:get(fMaxSize, TracerOpts, infinity)
, fEventFrame = maps:get(fEventFrame, TracerOpts, 100000)
, fMaxSize = maps:get(fMaxSize, TracerOpts, 104857600)
, fMaxMsg = maps:get(fMaxMsg, TracerOpts, 150)
, tcmIsCut = TcmIsCut
},
loop(State).
loop(State = #state{parent = Parent, size = Size, ioDevice = IoDevice, fEventFrame = MaxEvents, eventsThisFrame = NumEvents0, buffer = Buffer0, tcmIsCut = TcmIsCut}) ->
loop(#state{parent = Parent, size = Size, fDir = FDir, fBaseName = FBaseName, ioDevice = IoDevice, fMaxSize = MaxSize, tcmIsCut = TcmIsCut, fMaxMsg = FMaxMsg, msgCnt = MsgCnt, msgList = MsgList} = State) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State);
@ -69,31 +72,30 @@ loop(State = #state{parent = Parent, size = Size, ioDevice = IoDevice, fEventFra
terminate(Reason, State);
RMsg ->
Msg = case TcmIsCut of true -> tpTermCut:cut(RMsg); _ -> RMsg end,
Bin = term_to_binary(Msg),
BinSize = byte_size(Bin),
Buffer = <<Buffer0/binary, BinSize:32, Bin/binary>>,
NumEvents = NumEvents0 + 1,
if
MaxEvents =:= NumEvents ->
Frame = lz4f:compress_frame(Buffer),
ok = file:write(IoDevice, Frame),
maybe_rotate(State#state{size = Size + byte_size(Frame),
eventsThisFrame = 0, buffer = <<>>});
NewMsgList = [Msg | MsgList],
NewMsgCnt = MsgCnt + 1,
case NewMsgCnt >= FMaxMsg of
true ->
loop(State#state{eventsThisFrame = NumEvents, buffer = Buffer})
MsgListBin = zlib:compress(term_to_binary(NewMsgList)),
MsgListSize = byte_size(MsgListBin),
NewSize = Size + MsgListSize,
ok = file:write(IoDevice, [<<NewSize:32>>, MsgListBin]),
case NewSize >= MaxSize of
true ->
ok = file:close(IoDevice),
Filename = fileName(FDir, FBaseName),
{ok, NewIoDevice} = file:open(Filename, [write, raw]),
loop(State#state{ioDevice = NewIoDevice, size = 0, msgCnt = 0, msgList = []});
_ ->
loop(State#state{size = NewSize, msgCnt = 0, msgList = []})
end;
_ ->
loop(State#state{msgCnt = NewMsgCnt, msgList = NewMsgList})
end
end.
%%IMY-todo 使
maybe_rotate(State = #state{fBaseName = Filename, size = Size, fMaxSize = MaxSize,
ioDevice = OldIoDevice}) when Size > MaxSize ->
ok = file:close(OldIoDevice),
ok = file:rename(Filename, Filename ++ ".bak"),
{ok, NewIoDevice} = file:open(Filename, [write, raw]),
loop(State#state{size = 0, ioDevice = NewIoDevice});
maybe_rotate(State) ->
loop(State).
system_continue(_, _, State) ->
loop(State).
@ -105,6 +107,18 @@ system_code_change(Misc, _, _, _) ->
{ok, Misc}.
-spec terminate(any(), #state{}) -> no_return().
terminate(Reason, #state{ioDevice = IoDevice, buffer = Buffer}) ->
_ = file:write(IoDevice, lz4f:compress_frame(Buffer)),
terminate(Reason, #state{ioDevice = IoDevice, size = Size, msgList = MsgList}) ->
MsgListBin = zlib:compress(term_to_binary(MsgList)),
MsgListSize = byte_size(MsgListBin),
NewSize = Size + MsgListSize,
ok = file:write(IoDevice, [<<NewSize:32>>, MsgListBin]),
ok = file:close(IoDevice),
exit(Reason).
fileName(Dir, FBaseName) ->
{{Year, Month, Day}, {Hour, Minute, _Second}} = erlang:localtime(),
TimeStr = eFmt:formatBin("~B~2.10.0B~2.10.0B~B~2.10.0B_", [Year, Month, Day, Hour, Minute]),
FileName = <<TimeStr/binary, FBaseName/binary>>,
WholeName = filename:absname(filename:join(Dir, FileName)),
ok = filelib:ensure_dir(WholeName),
WholeName.

+ 112
- 0
src/tracer/tpTracerLog.erl 查看文件

@ -0,0 +1,112 @@
-module(tpTracerLog).
-include("eTpf.hrl").
-export([
start_link/1
, init/2
]).
%% sys callbacks
-export([
system_continue/3
, system_terminate/4
, system_code_change/4
]).
-record(state, {
parent :: pid()
, fDir :: file:filename_all() %% file base name
, fBaseName :: file:filename_all() %% file base name
, size = 0 :: non_neg_integer()
, fMaxSize :: infinity | non_neg_integer()
, ioDevice :: file:io_device()
, tcmIsCut = false
, fMaxLog :: pos_integer()
, fLogIndex = 0 :: non_neg_integer()
}).
start_link(Opts) ->
Pid = proc_lib:spawn_link(?MODULE, init, [self(), Opts]),
{ok, Pid}.
init(Parent, TracerOpts) ->
process_flag(message_queue_data, off_heap),
process_flag(trap_exit, true),
%% No need to close the file, it'll be closed when the process exits.
FDir = maps:get(fDir, TracerOpts, <<"./">>),
FBaseName = maps:get(fBaseName, TracerOpts, <<"traces.log">>),
Filename = fileName(FDir, FBaseName),
{ok, IoDevice} = file:open(Filename, [write, raw]),
TcmIsCut = maps:get(tcmIsCut, TracerOpts, maps:get(tcmDepth, ?defTcmMap)),
case TcmIsCut of
true ->
erlang:put(tcmDepth, maps:get(tcmDepth, TracerOpts, maps:get(tcmDepth, ?defTcmMap))),
erlang:put(tcmListSize, maps:get(tcmListSize, TracerOpts, maps:get(tcmListSize, ?defTcmMap))),
erlang:put(tcmMapSize, maps:get(tcmMapSize, TracerOpts, maps:get(tcmMapSize, ?defTcmMap))),
erlang:put(tcmTupleSize, maps:get(tcmTupleSize, TracerOpts, maps:get(tcmTupleSize, ?defTcmMap))),
erlang:put(tcmBinSize, maps:get(tcmBinSize, TracerOpts, maps:get(tcmBinSize, ?defTcmMap))),
erlang:put(tcmBitSize, maps:get(tcmBitSize, TracerOpts, maps:get(tcmBitSize, ?defTcmMap))),
erlang:put(tcmNestStruct, maps:get(tcmNestStruct, TracerOpts, maps:get(tcmNestStruct, ?defTcmMap)));
_ ->
ignore
end,
State = #state{
parent = Parent
, fDir = FDir
, fBaseName = FBaseName
, ioDevice = IoDevice
, fMaxSize = maps:get(fMaxSize, TracerOpts, 61644800)
, fMaxLog = maps:get(fMaxLog, TracerOpts, 50000)
, tcmIsCut = TcmIsCut
},
loop(State).
loop(#state{parent = Parent, fDir = FDir, fBaseName = FBaseName, size = Size, ioDevice = IoDevice, fMaxSize = MaxSize, fMaxLog = FMaxLog, fLogIndex = FLogIndex, tcmIsCut = TcmIsCut} = State) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State);
{'EXIT', Parent, Reason} ->
terminate(Reason, State);
RMsg ->
Msg = case TcmIsCut of true -> tpTermCut:cut(RMsg); _ -> RMsg end,
MsgBin = eFmt:format("~w", [Msg]),
ok = file:write(IoDevice, MsgBin),
NewSize = Size + byte_size(MsgBin),
NewFLogIndex = FLogIndex + 1,
case NewFLogIndex >= FMaxLog orelse NewSize >= MaxSize of
true ->
ok = file:close(IoDevice),
Filename = fileName(FDir, FBaseName),
{ok, NewIoDevice} = file:open(Filename, [write, raw]),
loop(State#state{size = 0, fLogIndex = 0, ioDevice = NewIoDevice});
_ ->
loop(State#state{size = NewSize, fLogIndex = NewFLogIndex})
end
end.
system_continue(_, _, State) ->
loop(State).
-spec system_terminate(any(), _, _, #state{}) -> no_return().
system_terminate(Reason, _, _, State) ->
terminate(Reason, State).
system_code_change(Misc, _, _, _) ->
{ok, Misc}.
-spec terminate(any(), #state{}) -> no_return().
terminate(Reason, #state{ioDevice = IoDevice}) ->
ok = file:close(IoDevice),
exit(Reason).
fileName(Dir, FBaseName) ->
{{Year, Month, Day}, {Hour, Minute, _Second}} = erlang:localtime(),
TimeStr = eFmt:formatBin("~B~2.10.0B~2.10.0B~B~2.10.0B_", [Year, Month, Day, Hour, Minute]),
FileName = <<TimeStr/binary, FBaseName/binary>>,
WholeName = filename:absname(filename:join(Dir, FileName)),
ok = filelib:ensure_dir(WholeName),
WholeName.

+ 2
- 3
src/utils/tpTermCut.erl 查看文件

@ -21,7 +21,7 @@ cut(Term, Depth) ->
TcmDepth = getTcmValue(tcmDepth),
case Depth > TcmDepth of
true ->
'$truncated11';
'$truncated';
_ ->
if
is_bitstring(Term) ->
@ -53,10 +53,9 @@ cutList([], _, _, _, _, _, Acc) ->
cutList([Term | Tail], Depth, Len, TcmTupleSize, TcmNestStruct, NumStructs, Acc) ->
case Len >= TcmTupleSize orelse NumStructs >= TcmNestStruct of
true ->
lists:reverse(['$truncated33' | Acc]);
lists:reverse(['$truncated' | Acc]);
_ ->
%% if List was a cons, Tail can be anything
io:format("IMY********************1111 ~p~n", [{Len, NumStructs, Term}]),
cutList(Tail, Depth, Len + 1, TcmTupleSize, TcmNestStruct, NumStructs + isStruct(Term), [cut(Term, Depth + 1) | Acc])
end;
cutList(Term, Depth, _, _, _, _, Acc) -> %% if List was a cons [[[a,a,a|b],1,2,3]|bbbb]

+ 1
- 1
test/lg_SUITE.erl 查看文件

@ -137,7 +137,7 @@ file_tracer_rotation(Config) ->
eTpf:trace(lists, tpTracerFile, #{
fBaseName => Prefix,
fMaxSize => 100, %% Intentionally low.
fEventFrame => 10 %% Needed to trigger the rotation, default is too high.
fMaxLog => 10 %% Needed to trigger the rotation, default is too high.
}),
lists:seq(1, 1000),
eTpf:stop(),

正在加载...
取消
保存