Pokazuje ślad stosu z działającej aplikacji Python


340

Mam tę aplikację Python, która od czasu do czasu zacina się i nie mogę się dowiedzieć, gdzie.

Czy jest jakiś sposób, aby zasygnalizować interpreterowi języka Python, aby pokazał dokładnie działający kod?

Jakiś rodzaj śledzenia stosu w locie?

Powiązane pytania:



Odpowiedzi:


315

Mam moduł, którego używam do takich sytuacji - gdzie proces będzie działał przez długi czas, ale czasami utknie z nieznanych i nieodwracalnych powodów. Jest nieco zhackowany i działa tylko na Uniksie (wymaga sygnałów):

import code, traceback, signal

def debug(sig, frame):
    """Interrupt running process, and provide a python prompt for
    interactive debugging."""
    d={'_frame':frame}         # Allow access to frame object.
    d.update(frame.f_globals)  # Unless shadowed by global
    d.update(frame.f_locals)

    i = code.InteractiveConsole(d)
    message  = "Signal received : entering python shell.\nTraceback:\n"
    message += ''.join(traceback.format_stack(frame))
    i.interact(message)

def listen():
    signal.signal(signal.SIGUSR1, debug)  # Register handler

Aby użyć, po prostu wywołaj funkcję Listen () w pewnym momencie, gdy program się uruchamia (możesz nawet umieścić go w site.py, aby wszystkie programy python go używały) i pozwolić mu działać. W dowolnym momencie wyślij procesowi sygnał SIGUSR1, używając kill lub w pythonie:

    os.kill(pid, signal.SIGUSR1)

Spowoduje to, że program przejdzie do konsoli Pythona w punkcie, w którym się aktualnie znajduje, pokazując ślad stosu i umożliwiając manipulowanie zmiennymi. Użyj control-d (EOF), aby kontynuować działanie (choć zauważ, że prawdopodobnie przerwiesz każde wejście / wyjście itp. W miejscu, w którym zasygnalizujesz, więc nie jest to w pełni nieinwazyjne.

Mam inny skrypt, który robi to samo, tyle że komunikuje się z uruchomionym procesem za pomocą potoku (aby umożliwić debugowanie procesów w tle itp.). Jest tu trochę za dużo, ale dodałem go jako przepis na książkę kucharską w języku Python .


1
Dzięki! Właśnie tego szukałem. Może mógłbyś również opublikować ten skrypt z obsługą potoku na stronie z fragmentami w języku Python?
Seb

2
Teraz opublikowałem go na stronie książki kucharskiej Pythona - dodano link.
Brian

1
Musiałem dodać „import readline”, aby włączyć funkcje historii.
miracle2k

2
Świetna wskazówka! Działa to również w celu wysłania sygnału do wszystkich procesów zawierających słowo „mypythonapp”: pkill -SIGUSR1 -f mypythonapp
Alexander

10
Jeśli aplikacja jest zablokowana, pętla interpretera języka Python może nie być w stanie uruchomić się w celu przetworzenia sygnału. Użyj faulthandlermodułu (i jego backportu znalezionego w PyPI) do obsługi sygnału poziomu C, która wydrukuje stos Pythona bez konieczności reagowania na pętlę interpretera.
gps

146

Sugestia zainstalowania modułu obsługi sygnałów jest dobra i bardzo go używam. Na przykład, bzr domyślnie instaluje moduł obsługi SIGQUIT , który wywołuje pdb.set_trace()natychmiastowe upuszczenie do monitu pdb . (Zobacz bzrlib.breakin Dokładne szczegóły znajdują się modułu .) Dzięki pdb możesz nie tylko uzyskać bieżący ślad stosu, ale także sprawdzić zmienne itp.

Czasami jednak muszę debugować proces, w którym nie miałem zdolności przewidywania, aby zainstalować moduł obsługi sygnału. W systemie Linux możesz dołączyć proces gdb do procesu i uzyskać ślad stosu Pythona za pomocą niektórych makr gdb. Umieścić http://svn.python.org/projects/python/trunk/Misc/gdbinit w ~/.gdbinit, a następnie:

  • Dołącz gdb: gdb -p PID
  • Uzyskaj śledzenie stosu python: pystack

Niestety nie jest całkowicie niezawodny, ale działa przez większość czasu.

Wreszcie, dołączanie straceczęsto daje dobry obraz tego, co robi proces.


2
Znakomity! Polecenie pystack czasami się blokuje, ale zanim to zrobi, daje mi kompletny ślad procesu w liniach kodu Pythona, bez potrzeby przygotowania.
muudscope

26
Drobna aktualizacja: ta technika gdb (i zaktualizowany kod) jest udokumentowana na wiki.python.org/moin/DebuggingWithGdb Na tym froncie wprowadzono pewne zmiany, udokumentowane pod tym adresem URL i najwyraźniej gdb 7 ma pewne wsparcie dla Pythona.
Nelson

7
O ile wiem, to działa naprawdę tylko wtedy, gdy masz symbole debugowania wkompilowane w twój plik binarny Pythona - np .: uruchomiłeś program z python2-dbg (na Ubuntu, jest to osobny pakiet python-dbg). Bez tych symboli nie wydaje się, aby uzyskać wiele użytecznych informacji.
drevicko

1
w moim przypadku ten powrót Unable to locate python framedo każdego polecenia
seriyPS

6
gdb 7+ - z obsługą Pythona zapewnia python-gdb.py. Więcej informacji tutaj: chezsoi.org/lucas/blog/2014/11/07/en-gdb-python-macros
Lucas Cimon

71

Prawie zawsze mam do czynienia z wieloma wątkami, a główny wątek na ogół niewiele robi, więc najciekawsze jest zrzucenie wszystkich stosów (co bardziej przypomina zrzut Java). Oto implementacja oparta na tym blogu :

import threading, sys, traceback

def dumpstacks(signal, frame):
    id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
    code = []
    for threadId, stack in sys._current_frames().items():
        code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

53

Uzyskiwanie śladu stosu nieprzygotowanego programu python, działającego w podstawowym pythonie bez symboli debugowania, można wykonać za pomocą pirazytu . Działa jak urok dla mnie w Ubuntu Trusty:

$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
$ sudo pyrasite 16262 dump_stacks.py # dumps stacks to stdout/stderr of the python program

(Hat tip do @Albert, którego odpowiedź zawierała między innymi wskaźnik do tego).


5
Działa to świetnie dla mnie, gdzie dump_stacks.pybył po prostuimport traceback; traceback.print_stack()
John Lehmann

2
traceback -lzawiera listę predefiniowanych skryptów Pythona, których możesz użyć, i dump_stacks.pyjest jednym z nich. Jeśli używasz własnego (na przykład do zapisywania śladu stosu w pliku), rozsądnie może być użycie innej nazwy.
johndodo,

12
Ważna wskazówka: uruchom apt-get install gdb python-dbg(lub odpowiednik) przed uruchomieniem pirazytu, w przeciwnym razie po cichu zawiedzie. W przeciwnym razie działa jak urok!
johndodo,

Ostatnie wydanie pirazytu miało miejsce w 2012 roku
Boris

35
>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

Możesz również ładnie sformatować ślad stosu, zobacz dokumenty .

Edycja : Aby zasymulować zachowanie Javy, zgodnie z sugestią @Douglas Leeder, dodaj:

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

do kodu startowego w aplikacji. Następnie możesz wydrukować stos, wysyłając SIGUSR1go do działającego procesu Python.


2
Spowoduje to wydrukowanie tylko śladu głównego wątku. Muszę jeszcze znaleźć rozwiązanie, aby zobaczyć ślady dla wszystkich wątków. W rzeczywistości w Pythonie brakuje interfejsu API do pobierania stosu z obiektu Thread, chociaż threading.enumerate () daje dostęp do wszystkich obiektów Thread.
haridsv

Działa to świetnie na cygwin. Drukuje tylko trzy linie śladu stosu, ale to wystarczy, aby uzyskać wskazówkę
slashdottir

28

Traceback Moduł posiada kilka ciekawych funkcji, wśród nich: print_stack:

import traceback

traceback.print_stack()

1
Aby zapisać ślad stosu do pliku, użyj: import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
GuruM

1
+1 do @gulgi za jego łatwą w użyciu odpowiedź. Niektóre inne odpowiedzi wyglądały na bardzo skomplikowane w przypadku mojego prostego zadania polegającego na uzyskaniu śladu stosu wywołań z funkcji skryptu.
GuruM,

24

Możesz wypróbować moduł obsługi błędów . Zainstaluj go za pomocą pip install faulthandleri dodaj:

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

na początku twojego programu. Następnie wyślij SIGUSR1 do swojego procesu (np .:), aby kill -USR1 42wyświetlić śledzenie wszystkich wątków Pythona na standardowym wyjściu. Przeczytaj dokumentację, aby uzyskać więcej opcji (np .: zaloguj się do pliku) i inne sposoby wyświetlania śledzenia.

Moduł jest teraz częścią Python 3.3. W przypadku Python 2 zobacz http://faulthandler.readthedocs.org/


20

To, co naprawdę mi pomogło, to wskazówka spiv (którą głosowałbym i komentowałbym, gdybym miał punkty reputacji), aby uzyskać ślad stosu z nieprzygotowanego procesu Pythona. Tyle że nie działało, dopóki nie zmodyfikowałem skryptu gdbinit . Więc:

  • pobierz http://svn.python.org/projects/python/trunk/Misc/gdbinit i włóż~/.gdbinit

  • edytuj, zmieniając PyEval_EvalFramenaPyEval_EvalFrameEx[edycja: nie jest już potrzebne; plik połączony ma już tę zmianę na dzień 14.01.2010]

  • Dołącz gdb: gdb -p PID

  • Uzyskaj śledzenie stosu python: pystack


Wydaje się, że gdbinit pod wspomnianym adresem URL ma łatkę, którą sugerujesz. W moim przypadku, gdy pisałem pystack, mój procesor właśnie się zawiesił. Nie pewny dlaczego.
Jesse Glick

2
Nie, nie ma - przepraszam, nie było jasne, ponieważ ta linia pojawia się w trzech miejscach. Łatka, którą powiązałem z, pokazuje, którą zmieniłem, kiedy zobaczyłem tę pracę.
Gunnlaugur Briem

2
Podobnie jak odpowiedź @ spiv, wymaga to, aby program działał pod Pythonem skompilowanym z symbolami debugowania. W przeciwnym razie dostanieszNo symbol "co" in current context.
Nickolay

12

Dodałbym to jako komentarz do odpowiedzi haridsv , ale brakuje mi reputacji, aby to zrobić:

Niektórzy z nas nadal tkwią w wersji Pythona starszej niż 2.6 (wymagana dla Thread.ident), więc dostałem kod działający w Pythonie 2.5 (choć bez wyświetlania nazwy wątku) jako taki:

import traceback
import sys
def dumpstacks(signal, frame):
    code = []
    for threadId, stack in sys._current_frames().items():
            code.append("\n# Thread: %d" % (threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

11

python -dv twójscript.py

To sprawi, że interpreter będzie działał w trybie debugowania i da ci ślad tego, co robi interpreter.

Jeśli chcesz interaktywnie debugować kod, powinieneś go uruchomić w następujący sposób:

python -m pdb yourscript.py

Mówi to interpreterowi Pythona o uruchomieniu skryptu z modułem „pdb”, który jest debuggerem Pythona, jeśli uruchomisz go w ten sposób, interpreter zostanie uruchomiony w trybie interaktywnym, podobnie jak GDB


To nie odpowiada na pytanie. Pytanie dotyczyło już uruchomionego procesu.
dbn

11

Spójrz na faulthandlermoduł, nowy w Pythonie 3.3. faulthandlerBackport do użytku w Pythonie 2 jest dostępny na PyPI.


2
Nowsza odpowiedź @haypo omawia to bardziej szczegółowo. Nie jestem pewien, jak zwykle jest to obsługiwane w przypadku SO, ale źle jest mieć dwie zasadniczo zduplikowane odpowiedzi ...
Nickolay

7

W systemie Solaris można używać pstack (1). Nie są wymagane żadne zmiany w kodzie Pythona. na przykład.

# pstack 16000 | grep : | head
16000: /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/serv
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:282 (_wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:295 (wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242 (block) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/_init_.py:249 (quickstart) ]
[ /usr/lib/pkg.depotd:890 (<module>) ]
[ /usr/lib/python2.6/threading.py:256 (wait) ]
[ /usr/lib/python2.6/Queue.py:177 (get) ]
[ /usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142 (run) ]
[ /usr/lib/python2.6/threading.py:477 (run)
etc.

2
Wydaje się, że istnieje program Debian / Ubuntu, pstackktóry robi to samo
Rory

1
Wydaje się, że daje jedynie ślad wstecz w Linuksie, a nie ślad zwrotny Pythona z nazwą pliku i numerami linii.
ogrisel

6

Jeśli korzystasz z systemu Linux, skorzystaj z niesamowitości gdbdzięki rozszerzeniom debugowania Pythona (może być w python-dbglub w python-debuginfopakiecie). Pomaga również w aplikacjach wielowątkowych, aplikacjach GUI i modułach C.

Uruchom swój program z:

$ gdb -ex r --args python <programname>.py [arguments]

To instruuje, jak gdbgo przygotować python <programname>.py <arguments>i rusunąć.

Teraz, kiedy program zawiesi się, przełącz się na gdbkonsolę, naciśnij Ctr+Ci wykonaj:

(gdb) thread apply all py-list

Zobacz przykładową sesję i więcej informacji tutaj i tutaj .


6

Przez jakiś czas szukałem rozwiązania do debugowania moich wątków i znalazłem je tutaj dzięki haridsv. Używam nieco uproszczonej wersji wykorzystującej traceback.print_stack ():

import sys, traceback, signal
import threading
import os

def dumpstacks(signal, frame):
  id2name = dict((th.ident, th.name) for th in threading.enumerate())
  for threadId, stack in sys._current_frames().items():
    print(id2name[threadId])
    traceback.print_stack(f=stack)

signal.signal(signal.SIGQUIT, dumpstacks)

os.killpg(os.getpgid(0), signal.SIGQUIT)

Na moje potrzeby filtruję wątki według nazwy.


3

Warto spojrzeć na Pydb , „rozszerzoną wersję debugera Pythona luźno opartą na zestawie poleceń gdb”. Obejmuje menedżerów sygnałów, którzy mogą zadbać o uruchomienie debuggera po wysłaniu określonego sygnału.

Projekt Summer of Code z 2006 r. Dotyczył dodania funkcji zdalnego debugowania do pydb w module o nazwie mpdb .


Wygląda na to, że przeszły dwa ( 1 ) przepisania ( 2 ) bez dodawania funkcji załączania przez PID, której szukałem ...
Nickolay

3

Zhakowałem razem narzędzie, które dołącza się do działającego procesu Pythona i wstrzykuję trochę kodu, aby uzyskać powłokę Pythona.

Zobacz tutaj: https://github.com/albertz/pydbattach


1
Uwaga: nie jest oczywiste, jak to zbudować. Dzięki za linki, które umieściłeś w README: pyrasitedziałało idealnie!
Nickolay

3

Można to zrobić za pomocą doskonałego py-spy . Jest to profiler do próbkowania dla programów w języku Python , więc jego zadaniem jest dołączanie do procesów w języku Python i próbkowanie stosów wywołań. Dlatego py-spy dump --pid $SOME_PIDwszystko, co musisz zrobić, aby zrzucić stosy wywołań wszystkich wątków w tym $SOME_PIDprocesie. Zazwyczaj potrzebuje eskalowanych uprawnień (do odczytu pamięci procesu docelowego).

Oto przykład tego, jak to wygląda w wątkowej aplikacji Python.

$ sudo py-spy dump --pid 31080
Process 31080: python3.7 -m chronologer -e production serve -u www-data -m
Python v3.7.1 (/usr/local/bin/python3.7)

Thread 0x7FEF5E410400 (active): "MainThread"
    _wait (cherrypy/process/wspbus.py:370)
    wait (cherrypy/process/wspbus.py:384)
    block (cherrypy/process/wspbus.py:321)
    start (cherrypy/daemon.py:72)
    serve (chronologer/cli.py:27)
    main (chronologer/cli.py:84)
    <module> (chronologer/__main__.py:5)
    _run_code (runpy.py:85)
    _run_module_as_main (runpy.py:193)
Thread 0x7FEF55636700 (active): "_TimeoutMonitor"
    run (cherrypy/process/plugins.py:518)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
Thread 0x7FEF54B35700 (active): "HTTPServer Thread-2"
    accept (socket.py:212)
    tick (cherrypy/wsgiserver/__init__.py:2075)
    start (cherrypy/wsgiserver/__init__.py:2021)
    _start_http_thread (cherrypy/process/servers.py:217)
    run (threading.py:865)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
...
Thread 0x7FEF2BFFF700 (idle): "CP Server Thread-10"
    wait (threading.py:296)
    get (queue.py:170)
    run (cherrypy/wsgiserver/__init__.py:1586)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)  

2

pyringe to debugger, który może wchodzić w interakcje z uruchomionymi procesami Pythona, drukować ślady stosu, zmienne itp. bez konieczności wcześniejszej konfiguracji.

Chociaż w przeszłości często korzystałem z rozwiązania do obsługi sygnałów, w niektórych środowiskach wciąż może być trudno odtworzyć problem.


3
Najwyraźniej jest niekompatybilny z niektórymi kompilacjami gdb (np. , którą zainstalowałem na ubuntu): github.com/google/pyringe/issues/16 , wymagając ręcznej przebudowy. Kolejny debugger pyrasitedziałał dla mnie jak urok.
Nickolay,

1

Nie ma sposobu, aby podłączyć się do uruchomionego procesu Pythona i uzyskać rozsądne wyniki. To, co robię, jeśli procesy się blokują, to podłączanie się do sieci i próba ustalenia, co dokładnie się dzieje.

Niestety często strace jest obserwatorem, który „naprawia” warunki wyścigu, dzięki czemu dane wyjściowe są również bezużyteczne.


1
Tak, to prawda. Szkoda, że ​​pdb nie obsługuje dołączania się do uruchomionego procesu ...
Bartosz Radaczyński

To nie jest prawda. Zobacz odpowiedź „spiv” powyżej, która pokazuje, jak podłączyć gdb i uzyskać ślad stosu Pythona.
Andrew Cooke

To nie to samo - te makra gdb nie są niezawodne i nie zapewniają pełnego / znanego interfejsu pdb. Często żałuję, że ktoś nie napisał małej aplikacji, która użyłaby ptrace do wstrzyknięcia kodu bajtowego Pythona do działającego procesu Pythona i spowodowania, aby wykonał „import pdb; pdb.set_trace () ', może także po tymczasowym przekierowaniu sys.stdin / stdout.
Marius Gedminas

To już nie jest prawda, zobacz inne odpowiedzi wskazujące na strzykawkę / pirasyt.
Nickolay

1

W tym celu można użyć PuDB , debuggera w języku Python z interfejsem programu curses. Poprostu dodaj

from pudb import set_interrupt_handler; set_interrupt_handler()

do swojego kodu i użyj Ctrl-C, gdy chcesz się zepsuć. Możesz kontynuować ci przerwać ponownie wiele razy, jeśli go przegapisz i chcesz spróbować ponownie.


Kiedy używasz powyższej komendy w django, nie zapomnij poprawnie uruchomić serwera, aby zapobiec usterkom: „manage.py runserver --noreload --nothreading”
potar

1

Jestem w obozie GDB z rozszerzeniami python. Postępuj zgodnie z https://wiki.python.org/moin/DebuggingWithGdb , co oznacza

  1. dnf install gdb python-debuginfo lub sudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

Weź również pod uwagę info threadsi thread apply all py-bt.


Czy to normalne, aby uzyskać odpowiedź, jak Traceback (most recent call first): Python Exception <class 'gdb.error'> No frame is currently selected.: Error occurred in Python command: No frame is currently selected.podczas pracy py-btw gdb?
crookedleaf

1
nieważne. to dlatego, że moja aplikacja działała jako sudo. gdb pyton <pid>Musiałem też uruchomić jako sudo.
crookedleaf,

1

Jak debugować dowolną funkcję w konsoli :

Utwórz funkcję, w której używasz pdb.set_trace () , a następnie funkcję, którą chcesz debugować.

>>> import pdb
>>> import my_function

>>> def f():
...     pdb.set_trace()
...     my_function()
... 

Następnie wywołaj utworzoną funkcję:

>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb) 

Miłego debugowania :)


0

Nie znam niczego podobnego do odpowiedzi Javy na SIGQUIT , więc może być konieczne wbudowanie go w aplikację. Może mógłbyś stworzyć serwer w innym wątku, który może uzyskać ślad stosu w odpowiedzi na wiadomość?


0

użyj modułu inspekcji.

import inspect help (inspect.stack) Pomoc na stosie funkcji w module inspect:

stos (kontekst = 1) Zwraca listę rekordów stosu ponad ramką dzwoniącego.

Uważam to za bardzo pomocne.


0

W Pythonie 3 pdb automatycznie zainstaluje moduł obsługi sygnałów przy pierwszym użyciu c (ont (inue)) w debuggerze. Naciśnięcie przycisku Control-C spowoduje powrót do tego miejsca. W Pythonie 2 znajduje się jedna linijka, która powinna działać nawet w stosunkowo starych wersjach (testowana w wersji 2.7, ale sprawdziłem źródła Pythona z powrotem do wersji 2.4 i wyglądało to dobrze):

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

pdb warto się nauczyć, jeśli poświęcasz trochę czasu na debugowanie Pythona. Interfejs jest nieco tępy, ale powinien być znany każdemu, kto używał podobnych narzędzi, takich jak gdb.


0

Jeśli musisz to zrobić z uWSGI, ma on wbudowany Python Tracebacker i wystarczy włączyć go w konfiguracji (numer jest dołączony do nazwy dla każdego pracownika):

py-tracebacker=/var/run/uwsgi/pytrace

Po wykonaniu tej czynności możesz wydrukować ślad, po prostu podłączając się do gniazda:

uwsgi --connect-and-read /var/run/uwsgi/pytrace1

0

W miejscu, w którym kod jest uruchamiany, możesz wstawić ten mały fragment kodu, aby zobaczyć ładnie sformatowany ślad wydruku stosu. Zakłada się, że masz folder o nazwie logsw katalogu głównym projektu.

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.