Запросить процесс Эрланга для его состояния?

Распространенным шаблоном в Erlang является рекурсивный цикл, который поддерживает состояние:

loop(State) ->
  receive
    Msg ->
      NewState = whatever(Msg),
      loop(NewState)
  end.

Есть ли способ запросить состояние запущенного процесса с помощью BIF или трассировки или что-то? Поскольку сообщения о сбоях говорят "... когда состояние было..." и показывают состояние сбойного процесса, я думал, что это будет легко, но я был разочарован тем, что не смог найти bif для этого.

Итак, я подумал, что использование трассировки модуля dbg сделает это. К сожалению, я полагаю, потому что эти циклы оптимизированы с помощью хвостового вызова, dbg будет захватывать только первый вызов функции.

Любое решение?

6 ответов

Решение

Если ваш процесс использует OTP, достаточно сделать sys:get_status(Pid),

Сообщение об ошибке, которое вы упоминаете, отображается SASL. SASL - это демон отчетов об ошибках в OTP.

Состояние, на которое вы ссылаетесь в своем примере кода, является просто аргументом хвостовой рекурсивной функции. Нет способа извлечь его, используя что-либо, кроме отслеживания BIF. Я думаю, что это не будет правильным решением в рабочем коде, поскольку трассировка предназначена для использования только в целях отладки.

Правильное и проверенное отраслью решение будет широко использовать OTP в вашем проекте. Тогда вы сможете в полной мере воспользоваться отчетами об ошибках SASL, rb модуль для сбора этих отчетов, sys - проверить состояние запущенного OTP-совместимого процесса, proc_lib - сделать недолговечные процессы OTP-совместимыми и т. д.

Оказывается, есть лучший ответ, чем все эти, если вы используете OTP:

sys:get_state/1

Вероятно, этого не было в то время.

Похоже, вы делаете проблему из ничего. erlang:process_info/1 дает достаточно информации для целей отладки. Если вашему ДЕЙСТВИТЕЛЬНО нужны аргументы функции цикла, почему бы вам не вернуть его вызывающей стороне в ответ на одно из специальных сообщений, которые вы определяете сами?

ОБНОВЛЕНИЕ: просто, чтобы уточнить терминологию. Наиболее близким к "состоянию процесса" на уровне языка является словарь процессов, использование которого крайне не рекомендуется. Это может быть запрошено с помощью 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>                                                                                           
{status,Pid,_,[_,_,_,_,[_,_,{data,[{_,State}]}]]} = sys:get_status(Pid).

Это то, что я использую, чтобы получить состояние gen_server. (Попытка добавить это как комментарий к ответу выше, но не смогла получить правильное форматирование.)

Насколько я знаю, вы не можете получить аргументы, передаваемые в локально вызываемую функцию. Я хотел бы, чтобы кто-то доказал, что я не прав.

-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) звонки в вашем коде временно. Бедный отладка.

Это "oneliner", который можно использовать в оболочке.

sys:get_status(list_to_pid("<0.1012.0>")).

Это поможет вам преобразовать строку pid в Pid.

Другие вопросы по тегам