Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

EINTR - sztuczne generowanie

3 views
Skip to first unread message

heby

unread,
Sep 9, 2020, 3:16:52 AM9/9/20
to
Cześć.

Potrzebuję w celach testowych zasypać aplikację errorami EINTR.
Aplikacja robi bardzo duzo operacji read/write i w ten sposób chciałbym
zwiększyśc prawdopodobieństwo znalezienia miejsca bez obsługi EINTR.

Mam pid mojego procesu. W jaki sposób mogę spowodować że w procesie tym
znacząco zwiększy się ilośc EINTR w przypadkowych miejscach?

Rozumiem że mam wysyłać sygnały do mojego procesu. Można to zrobic za
pomocą kill? I jaki sygnał mam wysyłać? Dowolny? np SIGCONT albo SIGALRM
będzie ok?

Mateusz Viste

unread,
Sep 9, 2020, 4:18:32 AM9/9/20
to
To byłoby pewnie dobre, gdybyś chciał testować funkcje read(), write(),
send(), itp na okoliczność poprawnej obsługi sygnałów. Tobie jednak
zależy raczej na sprawdzeniu swojego własnego kodu, który jest klientem
tych funkcji. Ja zrobiłbym to tak:


ssize_t monkey_write(int fd, const void *buf, size_t cnt) {
static int crazy_monkey;
crazy_monkey ^= 1;
if (crazy_monkey == 0) {
errno = EINTR;
return(-1);
}
return(write(fd, buf, cnt));
}

#define write monkey_write

(tutaj dalszy kod programu)


Mateusz

heby

unread,
Sep 9, 2020, 4:53:55 AM9/9/20
to
On 09/09/2020 10:18, Mateusz Viste wrote:
> To byłoby pewnie dobre, gdybyś chciał testować funkcje read(), write(),
> send(), itp na okoliczność poprawnej obsługi sygnałów. Tobie jednak
> zależy raczej na sprawdzeniu swojego własnego kodu, który jest klientem
> tych funkcji. Ja zrobiłbym to tak:
> ssize_t monkey_write(int fd, const void *buf, size_t cnt) {

Wchodzi to w grę, ale wymaga ingerencji w kod. Ja zaś myślę nad
*automatyzacją* zagadnienia na testach tak, aby sprawdzić nie tylko mój
kod, ale i kod biblitek 3rd party na które nie mam do końca wpływu.

Mateusz Viste

unread,
Sep 9, 2020, 5:03:01 AM9/9/20
to
2020-09-09 o 10:53 +0200, heby napisał:
> On 09/09/2020 10:18, Mateusz Viste wrote:
> > To byłoby pewnie dobre, gdybyś chciał testować funkcje read(),
> > write(), send(), itp na okoliczność poprawnej obsługi sygnałów.
> > Tobie jednak zależy raczej na sprawdzeniu swojego własnego kodu,
> > który jest klientem tych funkcji. Ja zrobiłbym to tak:
> > ssize_t monkey_write(int fd, const void *buf, size_t cnt) {
>
> Wchodzi to w grę, ale wymaga ingerencji w kod.

Niekoniecznie. Tzn. tak - wersja z monkey_write na pewno, ale można
wystrugać alternatywę w postaci własnej wersji write(), do wpięcia
podczas link time, zastępując tym samym read/write z libc. Ew.
podstawić swoje własne, zhackowane libc.

Opcja z kill oczywiście też wydaje się koszerna, ale trzeba będzie tym
killem ostro naparzać, żeby mieć szansę trafić w okienko kiedy
aplikacja akurat próbuje coś czytać/pisać... Niektóre krótkie operacje
mogą nigdy nie trafić w sygnał.

Mateusz

Queequeg

unread,
Sep 9, 2020, 6:03:01 AM9/9/20
to
Mateusz Viste <mat...@xyz.invalid> wrote:

> ssize_t monkey_write(int fd, const void *buf, size_t cnt) {
> static int crazy_monkey;

Hmm, jesteś pewien, że statyczne, ale lokalne zmienne, są inicjalizowane
na 0?

Zawsze myślałem, że dotyczy to tylko globalnych zmiennych statycznych, ale
teraz czytam i w sumie już nie wiem.

--
Jeśli będziesz tak lotny jak pszczółka
Tak silny jak niedźwiedź
Jeśli będziesz pracował jak koń
I do domu wracał tak zmęczony jak pies
Wtedy musisz pójść do weterynarza
Bo być może stałeś się już OSLEM.

heby

unread,
Sep 9, 2020, 6:04:05 AM9/9/20
to
On 09/09/2020 11:02, Mateusz Viste wrote:
> killem ostro naparzać, żeby mieć szansę trafić w okienko kiedy
> aplikacja akurat próbuje coś czytać/pisać... Niektóre krótkie operacje
> mogą nigdy nie trafić w sygnał.

Przy dziesiątkach tysięcy testów/h non-stop takie "przypadki" pojawiają
się nadspodziewanie często i mają jednocześnie brzydką cechę
niewystepowania in vitro, kiedy podpinasz debugger.

Chcę takie narzędzie do naparzania EINTR po to, aby:
a) złapać je na testach
b) przyspieszyć łapanie lokalne, z debugiem.

Mateusz Viste

unread,
Sep 9, 2020, 7:27:27 AM9/9/20
to
2020-09-09 o 10:03 +0000, Queequeg napisał:
> Mateusz Viste <mat...@xyz.invalid> wrote:
>
> > ssize_t monkey_write(int fd, const void *buf, size_t cnt) {
> > static int crazy_monkey;
>
> Hmm, jesteś pewien, że statyczne, ale lokalne zmienne, są
> inicjalizowane na 0?

Tak.

> Zawsze myślałem, że dotyczy to tylko globalnych zmiennych
> statycznych, ale teraz czytam i w sumie już nie wiem.

Może w tych nowoczesnych wynalazkach z plusami coś się zmieniło, nie
wiem. Ja żyję w 1990 i dobrze mi tu.

ISO/IEC 9899:1990(E), rozdział 6.5.7:

"If an object that has static storage duration is not initialized
explicitly, it is initialized implicitly as if every member that has
arithmetic type were assigned 0 and every member that has pointer type
were assigned a null pointer constant."

Żadnych udziwnień typu "tak, ale tylko jeśli..." nie widzę.


Mateusz

Queequeg

unread,
Sep 10, 2020, 6:00:25 AM9/10/20
to
Mateusz Viste <mat...@xyz.invalid> wrote:

> Może w tych nowoczesnych wynalazkach z plusami coś się zmieniło,

Raczej nie, moje niedopatrzenie. W sumie bardzo rzadko używam static
storage.

--
Zając dowiedział się że jego synowi ktoś w knajpie wybił wszystkie
zęby. Wchodzi do knajpy i mówi:
- Kto mojemu synowi wybił wszystkie żeby?
Wstaje niedźwiedź:
- Ja! Bo co?
- Ano, chciałem się spytać gdzie można znaleźć dobrego dentystę...

michal....@bofc.pl

unread,
Oct 19, 2020, 8:53:58 PM10/19/20
to
Do takich rzeczy najlepiej zrobić sobie MOCKi. W C najprościej zrobić to
pisząc sobie osobną libkę np libmockread.so, i w kodzie libki zdefiniować
sobie symbol read() (z takimi samymi argumentami jak read(2)) i potem
wziąć wskaźnik na swojego stuba read() oraz wrapera z glibc read(2).
Np. coś takiego

void *g_real_function = dlsym(RTLD_NEXT, read);
/* mock funkcji read - przykryje ona read(2) z glibc */
ssize_t read(int a0, void *a1, size_t a2) {
if (g_no_error) {
ssize_t (*real)(int, void *, size_t);
real = g_real_function;
return real(a0, a1, a2);
}
errno = EINTR;
return (ssize_t)-1;
}

Potem skompilować libkę

gcc -shared -fPIC -Wl,-soname,readmock.1 -o libreadmock.so readmock.c -lc -ldl

i zrobić preload na końcu

LD_PRELOAD=./libreadmock.so ./unit-tests

W unit testach można sobie potem ustawiać g_no_error na 1 albo 0 w zależności
czy chcemy aby następny read(2) wywalił błąd.

<shameless-plug>
Też kiedyś stałem przed takim problemem aż w końcu napisałem sobie generator
co mi tworzy odpowiedni plik C z potrzebnymi mockami oraz prostym API
do konfigurowania kiedy i jaki błąd ma wystąpić (np EINTR dokładnie po 4
wywołaniu read(2)). A co najważniejsze, przy takim podejściu mozna testować
nie robiąć jakichkolwiek zmian w kodzie produkcyjnym

Program/libkę znajdziesz tutaj:
https://libfo.bofc.pl/index.html
https://git.bofc.pl/libfo/
</shameless-plug>

--
.-----------------.-------------------.---------------------.------------------.
| Michal Lyszczek | Embedded C, Linux | Company Address | .-. open source |
| +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 | oo| supporter |
| https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\ & |
| GPG FF1EBFE7E3A974B1 | Bits of Code | NIP: 813 349 58 78 |(\_;/) programer |
`----------------------^--------------^---------------------^------------------'
signature.asc
0 new messages