Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 12

Ang materyal ng artikulo ay kinuha mula sa aking zen channel.

Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 12

Sa huli Artikulo, nangako akong isaalang-alang ang isyu ng pagtatasa ng load sa ticker at mga paraan upang labanan ang labis na pag-load ng computing sa media streamer. Ngunit napagpasyahan kong mas lohikal na saklawin ang mga isyu ng pag-debug ng mga filter ng craft na nauugnay sa paggalaw ng data at pagkatapos lamang isaalang-alang ang mga isyu ng pag-optimize ng pagganap.

Pag-debug ng mga filter ng craft

Pagkatapos naming tingnan ang mekanismo para sa paglipat ng data sa isang media streamer sa nakaraang artikulo, magiging lohikal na pag-usapan ang tungkol sa mga panganib na nakatago dito. Ang isa sa mga tampok ng prinsipyo ng "daloy ng data" ay ang memorya ay inilalaan mula sa heap sa mga filter na matatagpuan sa pinagmulan ng daloy ng data, at ang memorya ay pinalaya at ibinalik sa heap ng mga filter na matatagpuan sa dulo ng daloy. landas. Bilang karagdagan, ang paglikha ng bagong data at ang pagkasira nito ay maaaring mangyari sa isang lugar sa pagitan. Sa pangkalahatan, ang pagpapalabas ng memorya ay ginagawa ng ibang filter kaysa sa lumikha ng data block.

Mula sa punto ng view ng transparent na pagsubaybay sa memorya, magiging makatwiran para sa filter, sa pagtanggap ng isang input block, na agad itong sirain pagkatapos ng pagproseso, pagpapalaya ng memorya, at upang i-output ang isang bagong nilikha na bloke na may output data. Sa kasong ito, ang isang pagtagas ng memorya sa filter ay madaling masubaybayan - kung ang analyzer ay nakakita ng isang pagtagas sa filter, kung gayon ang susunod na filter ay hindi masisira nang maayos ang mga papasok na bloke at mayroong isang error sa loob nito. Ngunit mula sa punto ng view ng pagpapanatili ng mataas na pagganap, ang diskarte na ito sa pagtatrabaho sa mga bloke ng data ay hindi produktibo - humahantong ito sa isang malaking bilang ng mga operasyon upang maglaan/libreng memory para sa mga bloke ng data nang walang anumang kapaki-pakinabang na output.

Para sa kadahilanang ito, ang mga filter ng media streamer, upang hindi mapabagal ang pagpoproseso ng data, ay gumagamit ng mga function na lumilikha ng mga madaling kopya kapag kinokopya ang mga mensahe (napag-usapan namin ang tungkol sa mga ito sa nakaraang artikulo). Lumilikha lamang ang mga function na ito ng bagong instance ng header ng mensahe sa pamamagitan ng "pag-attach" dito ng isang bloke ng data mula sa "lumang" mensahe na kinokopya. Bilang resulta, dalawang header ang nakakabit sa isang data block at ang reference counter sa data block ay dinadagdagan. Ngunit magmumukha itong dalawang mensahe. Maaaring magkaroon ng higit pang mga mensahe na may tulad na "socialized" na bloke ng data, halimbawa, ang MS_TEE na filter ay bumubuo ng isang dosenang mga ganoong magaan na kopya nang sabay-sabay, na ipinamamahagi ang mga ito sa mga output nito. Kung gumagana nang tama ang lahat ng mga filter sa chain, sa pagtatapos ng pipeline, dapat umabot sa zero ang reference counter na ito at tatawagin ang memory release function: ms_free(). Kung ang tawag ay hindi nangyari, ang piraso ng memorya na ito ay hindi na ibabalik sa heap, i.e. ito ay "leak". Ang presyo para sa paggamit ng mga magaan na kopya ay ang pagkawala ng kakayahang madaling matukoy (tulad ng mangyayari sa mga regular na kopya) kung aling graph filter ang tumatagas ng memorya.

Dahil ang mga developer ng media streamer ang may pananagutan sa paghahanap ng mga pagtagas ng memorya sa mga native na filter, malamang na hindi mo na kailangang i-debug ang mga ito. Ngunit sa iyong craft filter, ikaw ang tipaklong ng iyong sariling kaligayahan, at ang oras na ginugugol mo sa paghahanap ng mga leaks sa iyong code ay depende sa iyong katumpakan. Upang bawasan ang iyong oras ng pag-debug, kailangan naming tingnan ang mga diskarte sa pag-detect ng leak kapag bumubuo ng mga filter. Bilang karagdagan, maaaring mangyari na ang pagtagas ay makikita lamang kapag ang filter ay inilapat sa isang tunay na sistema, kung saan ang bilang ng "mga suspek" ay maaaring napakalaki at ang oras para sa pag-debug ay limitado.

Paano nagpapakita ang isang memory leak mismo?

Ito ay lohikal na ipagpalagay na sa output ng programa tuktok ay magpapakita ng pagtaas ng porsyento ng memorya na inookupahan ng iyong aplikasyon.

Ang panlabas na pagpapakita ay na sa ilang mga punto ang system ay magsisimulang tumugon nang dahan-dahan sa paggalaw ng mouse at dahan-dahang i-redraw ang screen. Posible rin na ang log ng system ay lalago, na kumakain ng espasyo sa iyong hard drive. Sa kasong ito, ang iyong aplikasyon ay magsisimulang kumilos nang kakaiba, hindi tumugon sa mga utos, hindi makapagbukas ng file, atbp.

Upang matukoy ang paglitaw ng isang pagtagas, gagamit kami ng isang memory analyzer (pagkatapos dito ay tinutukoy bilang ang analyzer). Maaaring ito ay valgrind (mabuti artikulo tungkol dito) o nakapaloob sa compiler gcc MemorySanitizer o kung ano pa man. Kung ipinapakita ng analyzer na may naganap na pagtagas sa isa sa mga filter ng graph, nangangahulugan ito na oras na upang ilapat ang isa sa mga pamamaraan na inilarawan sa ibaba.

Paraan ng Three Pines

Gaya ng nabanggit sa itaas, kung mayroong memory leak, ituturo ng analyzer ang filter na humiling ng paglalaan ng memorya mula sa heap. Ngunit hindi nito ituturo ang filter na "nakalimutan" na ibalik ito, na, sa katunayan, ay ang salarin. Kaya, ang analyzer ay maaari lamang kumpirmahin ang aming mga takot, ngunit hindi ipahiwatig ang kanilang ugat.

Upang malaman ang lokasyon ng "masamang" filter sa graph, maaari kang pumunta sa pamamagitan ng pagbabawas ng graph sa pinakamababang bilang ng mga node kung saan nakita pa rin ng analyzer ang isang leak at i-localize ang problemang filter sa natitirang tatlong pine.

Ngunit maaaring mangyari na sa pamamagitan ng pagbawas sa bilang ng mga filter sa graph, maaabala mo ang normal na takbo ng pakikipag-ugnayan sa pagitan ng mga filter at iba pang elemento ng iyong system at hindi na lalabas ang pagtagas. Sa kasong ito, kakailanganin mong gumawa ng isang buong laki ng graph at gamitin ang diskarte na nakabalangkas sa ibaba.

Paraan ng sliding insulator

Para sa pagiging simple ng presentasyon, gagamit kami ng isang graph na binubuo ng isang hanay ng mga filter. Siya ay ipinapakita sa larawan.

Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 12

Isang regular na graph kung saan, kasama ng mga nakahandang media streamer na filter, apat na craft filter na F1...F4 ang ginagamit, ng apat na magkakaibang uri, na ginawa mo nang matagal na ang nakalipas at walang alinlangan sa kanilang kawastuhan. Gayunpaman, ipagpalagay natin na ang ilan sa kanila ay may mga memory leaks. Sa pagpapatakbo ng aming programa upang subaybayan ang analyzer, nalaman namin mula sa ulat nito na ang isang partikular na filter ay humiling ng isang tiyak na halaga ng memorya at hindi ito ibinalik sa heap N bilang ng beses. Madali mong mahulaan na magkakaroon ng isang link sa mga panloob na function ng filter ng uri ng MS_VOID_SOURCE. Ang kanyang gawain ay kumuha ng memorya mula sa bunton. Dapat ibalik ito ng ibang mga filter doon. Yung. malalaman natin ang katotohanan ng pagtagas.

Upang matukoy kung aling seksyon ng pipeline ang walang aksyon na humahantong sa isang memory leak, iminungkahi na magpakilala ng isang karagdagang filter na nagpapalipat lamang ng mga mensahe mula sa input patungo sa output, ngunit sa parehong oras ay lumilikha ng isang kopya ng input na mensahe na hindi magaan. , ngunit sa halip ay isang normal na "mabigat", pagkatapos ay ganap na tatanggalin ang mensahe na natanggap sa pasukan. Tatawagin namin ang naturang filter na isang insulator. Naniniwala kami na dahil simple ang filter, walang leakage dito. At isa pang positibong pag-aari - kung idaragdag namin ito saanman sa aming graph, hindi ito makakaapekto sa pagpapatakbo ng circuit sa anumang paraan. Ilarawan namin ang filter-isolator sa anyo ng isang bilog na may double circuit.

Binubuksan namin kaagad ang isolator pagkatapos ng filter ng voidsource:
Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 12

Pinapatakbo namin muli ang programa kasama ang analyzer, at nakita namin na sa pagkakataong ito ay sisisihin ng analyzer ang insulator. Pagkatapos ng lahat, siya na ngayon ang lumilikha ng mga bloke ng data, na pagkatapos ay nawala ng isang hindi kilalang pabaya na filter (o mga filter). Ang susunod na hakbang ay ilipat ang insulator sa kahabaan ng chain sa kanan, sa pamamagitan ng isang filter, at simulan muli ang pagsusuri. Kaya, hakbang-hakbang na paglipat ng isolator sa kanan, makakakuha tayo ng isang sitwasyon kung saan sa susunod na ulat ng analyzer ay bababa ang bilang ng mga "leaked" na bloke ng memorya. Nangangahulugan ito na sa hakbang na ito ang insulator ay nasa chain kaagad pagkatapos ng filter ng problema. Kung mayroon lamang isang "masamang" filter, ang pagtagas ay ganap na mawawala. Kaya, na-localize namin ang may problemang filter (o isa sa ilan). Sa pagkakaroon ng "fixed" ang filter, maaari naming ipagpatuloy ang paglipat ng isolator sa kanan sa kahabaan ng chain hanggang sa ganap naming talunin ang memory leaks.

Pagpapatupad ng isang filter ng isolator

Ang pagpapatupad ng isolator ay mukhang isang regular na filter. Header file:

/* Π€Π°ΠΉΠ» iso_filter.h  ОписаниС ΠΈΠ·ΠΎΠ»ΠΈΡ€ΡƒΡŽΡ‰Π΅Π³ΠΎ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°. */

#ifndef iso_filter_h
#define iso_filter_h

/* Π—Π°Π΄Π°Π΅ΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°. */
#include <mediastreamer2/msfilter.h>

#define MY_ISO_FILTER_ID 1024

extern MSFilterDesc iso_filter_desc;

#endif

Ang filter mismo:

/* Π€Π°ΠΉΠ» iso_filter.c  ОписаниС ΠΈΠ·ΠΎΠ»ΠΈΡ€ΡƒΡŽΡ‰Π΅Π³ΠΎ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°. */

#include "iso_filter.h"

    static void
iso_init (MSFilter * f)
{
}
    static void
iso_uninit (MSFilter * f)
{
}

    static void
iso_process (MSFilter * f)
{
    mblk_t *im;

    while ((im = ms_queue_get (f->inputs[0])) != NULL)
    {
        ms_queue_put (f->outputs[0], copymsg (im));
        freemsg (im);
    }
}

static MSFilterMethod iso_methods[] = {
    {0, NULL}
};

MSFilterDesc iso_filter_desc = {
    MY_ISO_FILTER_ID,
    "iso_filter",
    "A filter that reads from input and copy to its output.",
    MS_FILTER_OTHER,
    NULL,
    1,
    1,
    iso_init,
    NULL,
    iso_process,
    NULL,
    iso_uninit,
    iso_methods
};

MS_FILTER_DESC_EXPORT (iso_desc)

Paraan para sa pagpapalit ng mga function ng pamamahala ng memorya

Para sa mas banayad na pananaliksik, ang media streamer ay nagbibigay ng kakayahang palitan ang mga function ng pag-access sa memorya ng iyong sarili, na, bilang karagdagan sa pangunahing gawain, ay magtatala ng "Sino, saan at bakit". Tatlong function ang pinalitan. Ginagawa ito tulad ng sumusunod:

OrtpMemoryFunctions reserv;
OrtpMemoryFunctions my;

reserv.malloc_fun = ortp_malloc;
reserv.realloc_fun = ortp_realloc;
reserv.free_fun = ortp_free;

my.malloc_fun = &my_malloc;
my.realloc_fun = &my_realloc;
my.free_fun = &my_free;

ortp_set_memory_functions(&my);

Nakakatulong ang feature na ito sa mga kaso kung saan pinabagal ng analyzer ang pagpapatakbo ng mga filter kaya naabala ang operasyon ng system kung saan binuo ang ating circuit. Sa ganoong sitwasyon, kailangan mong iwanan ang analyzer at gumamit ng pagpapalit ng mga function para sa pagtatrabaho sa memorya.

Isinaalang-alang namin ang isang algorithm ng mga aksyon para sa isang simpleng graph na hindi naglalaman ng mga sangay. Ngunit ang diskarte na ito ay maaaring ilapat sa iba pang mga kaso, siyempre na may mas kumplikado, ngunit ang ideya ay nananatiling pareho.

Sa susunod na artikulo, titingnan natin ang isyu ng pagtatantya ng load sa isang ticker at mga paraan upang labanan ang labis na pagkarga ng computing sa isang media streamer.

Pinagmulan: www.habr.com

Magdagdag ng komento