Запросить процесс Erlang для его состояния?
Общий шаблон в Erlang - это рекурсивный цикл, который поддерживает состояние:
loop(State) ->
receive
Msg ->
NewState = whatever(Msg),
loop(NewState)
end.
Есть ли способ запросить состояние запущенного процесса с бифом или трассировкой или что-то еще? Поскольку сообщения о сбоях говорят "... когда состояние было..." и показывают разбитое состояние процесса, я думал, что это будет легко, но я был разочарован тем, что мне не удалось найти bif, чтобы сделать это.
Итак, я подумал, что использование трассировки модуля dbg сделает это. К сожалению, я считаю, что, поскольку эти циклы оптимизированы с помощью хвоста, dbg будет захватывать только первый вызов функции.
Любое решение?
Ответы
Ответ 1
Если ваш процесс использует OTP, достаточно сделать sys:get_status(Pid)
.
Сообщение об ошибке, которое вы упоминаете, отображается SASL. SASL представляет собой отчет об ошибках в OTP.
Состояние, которое вы указываете в вашем примере кода, всего лишь аргумент функции рекурсивной хвоста. Невозможно извлечь его, используя что-либо, кроме отслеживания BIF. Я думаю, это не было бы правильным решением в производственном коде, поскольку трассировка предназначена для использования только для целей отладки.
Соответствующее и проверенное в отрасли решение будет широко использовать OTP в вашем проекте. Затем вы можете в полной мере использовать отчет об ошибках SASL, модуль rb
для сбора этих отчетов, sys
- для проверки состояния работающего OTP-совместимого процесса, proc_lib
- для выполнения короткоживущих процессов OTP-совместимых, и др.
Ответ 2
Похоже, вы делаете проблему из ничего. erlang: process_info/1 дает достаточно информации для целей отладки. Если ваши ДЕЙСТВИТЕЛЬНО нужны аргументы функции цикла, почему бы вам не вернуть его вызывающему абоненту в ответ на одно из специальных сообщений, которые вы сами определяете?
UPDATE:
Просто уточнить терминологию. Самое близкое к "состоянию процесса" на языковом уровне - это словарь процессов, использование которого сильно обескуражено. Он может быть запрошен erlang: process_info/1 или erlang: process/2.
Вам действительно нужно отслеживать вызовы локальных функций процесса вместе с их аргументами:
-module(ping).
-export([start/0, send/1, loop/1]).
start() ->
spawn(?MODULE, loop, [0]).
send(Pid) ->
Pid ! {self(), ping},
receive
pong ->
pong
end.
loop(S) ->
receive
{Pid, ping} ->
Pid ! pong,
loop(S + 1)
end.
Консоль:
Erlang (BEAM) emulator version 5.6.5 [source] [smp:2] [async-threads:0] [kernel-poll:false]
Eshell V5.6.5 (abort with ^G)
1> l(ping).
{module,ping}
2> erlang:trace(all, true, [call]).
23
3> erlang:trace_pattern({ping, '_', '_'}, true, [local]).
5
4> Pid = ping:start().
<0.36.0>
5> ping:send(Pid).
pong
6> flush().
Shell got {trace,<0.36.0>,call,{ping,loop,[0]}}
Shell got {trace,<0.36.0>,call,{ping,loop,[1]}}
ok
7>
Ответ 3
Оказывается, лучший ответ, чем все, если вы используете OTP:
sys:get_state/1
Вероятно, этого не существовало в то время.
Ответ 4
Насколько я знаю, вы не можете передать аргументы локально вызываемой функции. Я хотел бы, чтобы кто-то доказал, что я неправ.
-module(loop).
-export([start/0, loop/1]).
start() ->
spawn_link(fun () -> loop([]) end).
loop(State) ->
receive
Msg ->
loop([Msg|State])
end.
Если мы хотим отслеживать этот модуль, вы делаете следующее в оболочке.
dbg:tracer().
dbg:p(new,[c]).
dbg:tpl(loop, []).
Используя этот параметр трассировки, вы увидите локальные вызовы ( "l" в tpl означает, что будут отслеживаться и локальные вызовы, а не только глобальные).
5> Pid = loop:start().
(<0.39.0>) call loop:'-start/0-fun-0-'/0
(<0.39.0>) call loop:loop/1
<0.39.0>
6> Pid ! foo.
(<0.39.0>) call loop:loop/1
foo
Как вы видите, только входящие вызовы включены. Нет аргументов в поле зрения.
Моя рекомендация - основывать правильность в отладке и тестировании отправленных сообщений, а не на состоянии, хранящемся в процессах. То есть если вы отправляете процесс кучу сообщений, утверждайте, что он поступает правильно, а не что он имеет определенный набор значений.
Но, конечно, вы также могли бы покрасить несколько вызовов erlang:display(State)
в свой код временно. Бедный человек отлаживает.
Ответ 5
{status,Pid,_,[_,_,_,_,[_,_,{data,[{_,State}]}]]} = sys:get_status(Pid).
Это то, что я использую для получения состояния gen_server. (Пытался добавить его в качестве комментария к ответу выше, но не смог правильно форматировать.)
Ответ 6
Это "oneliner", который можно использовать в оболочке.
sys:get_status(list_to_pid("<0.1012.0>")).
Это поможет вам преобразовать строку pid в Pid.