Utforska Mediastreamer2 VoIP-motorn. Del 12

Materialet i artikeln är hämtat från min zen kanal.

Utforska Mediastreamer2 VoIP-motorn. Del 12

Förr artikeln, jag lovade att överväga frågan om att bedöma belastningen på tickern och sätt att bekämpa överdriven datorbelastning i mediastreamern. Men jag bestämde mig för att det skulle vara mer logiskt att täcka frågorna om felsökning av hantverksfilter relaterade till datarörelse och först då överväga frågor om prestandaoptimering.

Felsökning av hantverksfilter

Efter att vi tittat på mekanismen för att flytta data i en mediastreamer i föregående artikel, skulle det vara logiskt att prata om farorna som är gömda i den. En av funktionerna i "dataflödes"-principen är att minne allokeras från högen i filter som är placerade vid källan till dataflödet, och minnet frigörs och återförs till högen av filter placerade i slutet av flödet väg. Dessutom kan skapandet av ny data och dess förstörelse ske någonstans däremellan. I allmänhet utförs minnesfrigöring av ett annat filter än det som skapade datablocket.

Ur synvinkel av transparent minnesövervakning skulle det vara rimligt att filtret, när det tar emot ett ingångsblock, omedelbart förstör det efter bearbetning, frigör minnet och matar ut ett nyskapat block med utdata. I det här fallet skulle en minnesläcka i filtret lätt kunna spåras - om analysatorn upptäckte en läcka i filtret, förstör nästa filter inte inkommande block ordentligt och det finns ett fel i det. Men ur synvinkeln att upprätthålla hög prestanda är detta tillvägagångssätt för att arbeta med datablock inte produktivt - det leder till ett stort antal operationer för att allokera/frigöra minne för datablock utan någon användbar utdata.

Av denna anledning använder mediastreamer-filter, för att inte sakta ner databehandlingen, funktioner som skapar enkla kopior när du kopierar meddelanden (vi pratade om dem i föregående artikel). Dessa funktioner skapar bara en ny instans av meddelandehuvudet genom att "bifoga" ett datablock från det "gamla" meddelandet som kopieras. Som ett resultat kopplas två rubriker till ett datablock och referensräknaren i datablocket inkrementeras. Men det kommer att se ut som två meddelanden. Det kan finnas fler meddelanden med ett sådant "socialiserat" datablock, till exempel genererar MS_TEE-filtret ett dussin av sådana lätta kopior på en gång, och distribuerar dem mellan dess utgångar. Om alla filter i kedjan fungerar korrekt, i slutet av pipelinen bör denna referensräknare nå noll och minnesfrigöringsfunktionen kommer att anropas: ms_free(). Om samtalet inte inträffar kommer denna minnesbit inte längre att återföras till högen, d.v.s. det kommer att "läcka". Priset för att använda lätta kopior är förlusten av förmågan att enkelt avgöra (som skulle vara fallet med vanliga kopior) vilket graffilter som läcker minne.

Eftersom utvecklarna av mediastreamer är ansvariga för att hitta minnesläckor i inbyggda filter, behöver du troligen inte felsöka dem. Men med ditt hantverksfilter är du gräshoppan av din egen lycka, och tiden du spenderar på att leta efter läckor i din kod kommer att bero på din noggrannhet. För att minska din felsökningstid måste vi titta på läckagedetekteringstekniker när vi utvecklar filter. Dessutom kan det hända att läckan kommer att visa sig först när filtret appliceras i ett riktigt system, där antalet "misstänkta" kan vara enormt och tiden för felsökning begränsad.

Hur visar sig en minnesläcka?

Det är logiskt att anta att i programmets utdata topp kommer att visa den ökande andelen minne som upptas av din applikation.

Den yttre manifestationen kommer att vara att systemet någon gång kommer att börja svara långsamt på musrörelser och sakta rita om skärmen. Det är också möjligt att systemloggen växer och tar upp utrymme på din hårddisk. I det här fallet kommer din applikation att börja bete sig konstigt, inte svara på kommandon, kan inte öppna en fil, etc.

För att upptäcka förekomsten av en läcka kommer vi att använda en minnesanalysator (nedan kallad analysatorn). Det kan vara valgrind (Bra artikel om det) eller inbyggd i kompilatorn gcc Memory Sanitizer eller något annat. Om analysatorn visar att en läcka uppstår i ett av graffiltren betyder det att det är dags att tillämpa någon av metoderna som beskrivs nedan.

Tre tallar metod

Som nämnts ovan, om det finns en minnesläcka, kommer analysatorn att peka på filtret som begärde minnestilldelning från högen. Men det kommer inte att peka ut filtret som "glömde" att returnera det, vilket faktiskt är boven. Således kan analysatorn bara bekräfta våra rädslor, men inte indikera deras rot.

För att ta reda på platsen för det "dåliga" filtret i grafen kan du gå genom att reducera grafen till det minsta antalet noder där analysatorn fortfarande upptäcker en läcka och lokalisera det problematiska filtret i de återstående tre tallarna.

Men det kan hända att genom att minska antalet filter i grafen kommer du att störa det normala förloppet av interaktion mellan filter och andra delar av ditt system och läckan kommer inte längre att visas. I det här fallet måste du arbeta med en graf i full storlek och använda metoden som beskrivs nedan.

Glidisoleringsmetod

För enkelhetens skull kommer vi att använda en graf som består av en kedja av filter. Hon visas på bilden.

Utforska Mediastreamer2 VoIP-motorn. Del 12

En vanlig graf där man, tillsammans med färdiga mediastreamerfilter, använder fyra hantverksfilter F1...F4, av fyra olika typer, som du gjort för länge sedan och inte tvivlar på deras riktighet. Låt oss dock anta att flera av dem har minnesläckor. När vi kör vårt program för att övervaka analysatorn, lär vi oss från dess rapport att ett visst filter begärde en viss mängd minne och inte returnerade det till högen N antal gånger. Du kan enkelt gissa att det kommer att finnas en länk till de interna filterfunktionerna av typen MS_VOID_SOURCE. Hans uppgift är att ta minnet från högen. Andra filter måste returnera det dit. De där. vi kommer att upptäcka läckan.

För att avgöra i vilken sektion av pipelinen det var passivitet som ledde till en minnesläcka, föreslås det att införa ett extra filter som helt enkelt flyttar meddelanden från ingång till utgång, men samtidigt skapar en kopia av ingångsmeddelandet som inte är lätt , utan snarare en normal "tung", raderar sedan helt meddelandet som mottogs vid ingången. Vi kommer att kalla ett sådant filter en isolator. Vi tror att eftersom filtret är enkelt så finns det inget läckage i det. Och ytterligare en positiv egenskap - om vi lägger till den någonstans i vår graf, kommer detta inte att påverka kretsens funktion på något sätt. Vi kommer att avbilda filterisolatorn i form av en cirkel med en dubbelkrets.

Vi slår på isolatorn omedelbart efter voidsource-filtret:
Utforska Mediastreamer2 VoIP-motorn. Del 12

Vi kör programmet med analysatorn igen, och vi ser att denna gång kommer analysatorn att skylla på isolatorn. Det är trots allt han som nu skapar datablock, som sedan går förlorade av ett okänt vårdslöst filter (eller filter). Nästa steg är att flytta isolatorn längs kedjan åt höger, med ett filter, och starta analysen igen. Så, steg för steg flytta isolatorn åt höger, kommer vi att få en situation där i nästa analysatorrapport kommer antalet "läckta" minnesblock att minska. Det betyder att isolatorn i detta steg var i kedjan direkt efter problemfiltret. Om det bara fanns ett "dåligt" filter, kommer läckan att försvinna helt. Således lokaliserade vi det problematiska filtret (eller ett av flera). Efter att ha "fixat" filtret kan vi fortsätta att flytta isolatorn till höger längs kedjan tills vi helt övervinner minnesläckor.

Implementering av ett isolatorfilter

Isolatorimplementeringen ser precis ut som ett vanligt filter. Rubrikfil:

/* Файл 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

Själva filtret:

/* Файл 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)

Metod för att ersätta funktioner för minneshantering

För mer subtil forskning ger mediastreamern möjligheten att ersätta minnesåtkomstfunktioner med dina egna, som förutom huvudarbetet kommer att spela in "Vem, var och varför". Tre funktioner byts ut. Detta görs på följande sätt:

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);

Denna funktion hjälper till i de fall där analysatorn bromsar filtrens funktion så mycket att funktionen av systemet som vår krets är byggd i störs. I en sådan situation måste du överge analysatorn och använda ersättning av funktioner för att arbeta med minne.

Vi har övervägt en algoritm för åtgärder för en enkel graf som inte innehåller grenar. Men detta tillvägagångssätt kan tillämpas på andra fall, naturligtvis med mer komplexitet, men idén förblir densamma.

I nästa artikel kommer vi att titta på frågan om att uppskatta belastningen på en ticker och sätt att bekämpa överdriven datorbelastning i en mediastreamer.

Källa: will.com

Lägg en kommentar