17 października 2015

[C++11] std::future - programowanie oparte na zadaniach

W programowaniu współbieżnym preferowanym podejściem powinno być programowanie oparte na zadnich, a nie na wątkach [1]. Kilka przydatnych linków:
std::future jest obiektem, przechowującym wyniki (także wyjątki) asynchronie działających funkcji - czyta wynik ze współdzielonego stanu. Wynik można pozyskać za pomocą metody get(). W takim scenariuszu wątek główny zostanie zablokowany do chwili aż wynik będzie dostępny, lub zwróci wynik natychmiast, jeżeli asynchroniczna operacja zakończyła się wcześniej.

Do asynchronicznego wołania funkcji służy std::async. Jest to rozwiązanie najbardziej wysokopoziomowe, gdyż sam std::async dba o to by ustawić std::future w stan ready. Wywołanie oprócz ewentualnych argumentów jakie mają być przekazane do funkcji wymaga również podania policy:
  • std::launch::async - nowy wątek zostanie uruchomiony do wykonania zadania
  • std::launch::deferred - wykonanie zadania zostaje odroczone do momentu wywołania metod get() lub wait()
  • std::launch::async | std::launch::deferred - kombinacja flag, jednak zachowanie jest zależne od implementacji
Wszystkie przykłady kompilowałem w ten sam sposób, z dodatkowo włączonym sanitizerem, który pomógł wykryć kilka błędów w kodzie.
$ clang++ -std=c++14 -fsanitize=thread -lpthread -g main.cpp
Przykład obliczania ciągu Fibonacciego z zastosowaniem std::async.
#include <iostream>
#include <future>

using namespace std;

int fibonacci(int n)
{
    std::cout << "Current n: " << n << std::endl;
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

int main()
{
    std::future<int> result = std::async(std::launch::async | std::launch::deferred,
                                         fibonacci, 3);
    std::cout << "Result: " << result.get() << std::endl;

    return 0;
}
Wynik:
Current n: 3
Current n: 2
Current n: 1
Current n: 0
Current n: 1
Result: 2
std::packaged_task także pozawala na tworzenie obiektów std::future. Można go porównać do std::function, a więc jako wrapper do tworzenia obiektów callable. W przeciwieństwie do std::async, nie uruchamia on jednak przekazanej funkcji automatycznie. Najważniejszą właściwością std::packaged_task jest możliwość pozyskania z niego obiektu std::future, i przekazania go do innego wątku, gdzie zostanie wykonany.

Przykład:
#include <iostream>
#include <thread>
#include <future>

using namespace std;

int fibonacci(int n)
{
    std::cout << "Current n: " << n << std::endl;
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

int main()
{
    std::packaged_task<int(int)> task{fibonacci};
    std::future<int> result = task.get_future();

    std::thread t{std::move(task), 3};
    t.join();

    std::cout << "Result: " << result.get() << std::endl;

    return 0;
}
Wynik:
Current n: 3
Current n: 2
Current n: 1
Current n: 0
Current n: 1
Result: 2
std::promise można rozważyć jako początek kanału komunikacyjnego, na którego końcu znajduje się std::future. Do jego zadań, należy zapisanie wyniku do współdzielonego stanu. Czasami dane, które nas interesują są już dostępne (obliczone) i nie musimy czekać na zakończenie jakiegoś wątku. Za pomocą std::promise możemy w wątku głównym wytworzyć obiekt std::future (czyli końcówka kanału - dzięki metodzie get_future()), a następnie przekazać go (std::promise) do innego wątku. W wątku pobocznym, gdy interesujące nas dane zostaną obliczone wołamy metodę set_value() (początek kanału). W ten sposób obiekt std::future zostanie ustawiony w stan ready, i dane będą mogły być odczytane w wątku głównym. W porównywaniu do std::async, jest to rozwiązaniem bardziej nisko poziomowe, lecz czasami zachodzi potrzeba aby z niego skorzystać.

std::promise ma jeszcze jedno zastosowanie, można go wykorzystać jako mechanizm do sygnalizacji pomiędzy wątkami (std::promise<void> - typ obiektu jako void). Coś na kształt std::condition_variable, z tym że taka sygnalizacja może zadziać się tylko raz. Aby odblokować pracę na współdzielonych danych należy wywołać set_value(). Aby wstrzymać pracę wątku do momentu, aż dane będą dostępne, należy wywołać metodę wait().

Przykład:
#include <iostream>
#include <thread>
#include <future>

using namespace std;

struct Fibonacci {
    std::promise<int> p;
    bool isSatisfied;
    const int index;
    int partialIndex;

    Fibonacci(std::promise<int> p_, int index_)
        : p(std::move(p_))
        , isSatisfied(false)
        , index(index_)
        , partialIndex(0)
    {
    }

    int fibonacci(int n)
    {
        std::cout << "Current n: " << n << std::endl;

        int result = 0;
        if (n == 0)
            result = 0;
        else if (n == 1)
            result = 1;
        else
            result = fibonacci(n - 1) + fibonacci(n - 2);

        if (isSatisfied == false and partialIndex == n) {
            isSatisfied = true;
            std::cout << "Passing partial result" << std::endl;

            p.set_value(result);
        }
        return result;
    }

    void operator()(int partialVal_)
    {
        partialIndex = partialVal_;
        fibonacci(index);
    }
};

int main()
{
    std::promise<int> p;
    std::future<int> result = p.get_future();

    std::thread t{ Fibonacci{ std::move(p), 5 }, 3 };

    std::cout << "Result: " << result.get() << std::endl;
    t.join();

    return 0;
}
Przykładowy wynik:
Current n: 5
Current n: 4
Current n: 3
Current n: 2
Current n: 1
Current n: 0
Current n: 1
Passing partial result
Current n: 2
Current n: 1
Current n: 0
Current n: 3
Current n: 2
Result: 2Current n: 
1
Current n: 0
Current n: 1
Bibliografia:
  • [1] Scotta Meyersa: Skuteczny nowoczesny C++. APN PROMISE SA, 2015. Rozdział 7, str. 289.
  • [2] Anthony Williams: C++ Concurency in Action. USA Manning publications Co., 2012. Rozdział 7, str. 67.

2 października 2015

[C++11] std::condition_variable i współdzielenie danych między wątkami

Praca na danych wymaga mechanizmu, dzięki któremu wątki będą mogły zablokować swoje działania do czasu, aż z danych nie będzie korzystał żaden inny wątek. Najprostszym i zarazem najmniej efektywnym rozwiązaniem może być pętla i próba założenia blokady na muteks. Biblioteka standardowa na szczęście oferuje znacznie lepsze rozwiązanie w postaci std::condition_variable.

std::condition_variable wymaga do działania muteksów i std::unique_lock (ze względu na efektywność). Pozwala to na większą elastyczność, gdyż muteks może zostać zablokowany i odblokowany w dowolnym momencie (std::lock_guard wykorzystuje RAII). Klasa posiada dwa rodzaje metod: wait_* (wait, waif_for, wait_until) oraz notify_* (notify_one, notify_all). Te pierwsze blokują działanie wątku do czasu, aż zostanie spełniony warunek który został przekazany do wait_*. Może to być lambda, punkt w czasie lub odcinek czasu. Natomiast notify_* zajmuje się wybudzaniem wątków.

Ciekawostka, o której dowiedziałem się z cppreference. Przed wołaniem notify_*, należy ręczenie zawołać unlock na obiekcie std::unique_lock (choć destruktor tego obiektu i tak by to wykonał). W ten sposób unikamy sytuacji, w której wybudzamy wątek, tylko po to by ponownie go uśpić, ponieważ warunek nie został jeszcze osiągnięty.
Przykład.
#include <condition_variable>
#include <thread>
#include <mutex>
#include <list>
#include <string>
#include <iostream>

using namespace std;

std::mutex m;
std::condition_variable cv;
std::list<string> train;

void dig_coal() {
    int resource = 4;
    while(resource >= 0) {
        std::unique_lock<std::mutex> l{m};
        cv.wait_for(l, std::chrono::milliseconds{400});

        if (resource > 0) train.push_back("coal");
        else train.push_back("empty");
        resource -= 1;
        std::cout << "Added trolley, train length: " << train.size() << std::endl;

        l.unlock();
        cv.notify_one();
    }

    std::cout << "No more coal to mining." << endl;
}

void burn_coal() {
    while(true) {
        std::unique_lock<std::mutex> l{m};
        cv.wait(l, []{ return not train.empty(); });

        const string trolley = train.front();
        train.pop_front();
        std::cout << "Coal burn, train length: " << train.size() << std::endl;

        l.unlock();
        cv.notify_one();

        if(trolley == "empty") break;
        std::this_thread::sleep_for(std::chrono::milliseconds{600});
    }

    std::cout << "All coal burn." << endl;
}

int main() {
    std::thread miner{dig_coal};
    std::thread power_station{burn_coal};

    cv.notify_one();

    miner.join();
    power_station.join();

    return 0;
}
Wynik:
Added trolley, train length: 1
Coal burn, train length: 0
Added trolley, train length: 1
Added trolley, train length: 2
Coal burn, train length: 1
Added trolley, train length: 2
Added trolley, train length: 3
No more coal to mining.
Coal burn, train length: 2
Coal burn, train length: 1
Coal burn, train length: 0
All coal burn.

1 października 2015

kompilacja clang-a i libc++

Z kolejnymi wydaniami, pojawiają się delikatne różnice w procesie instalacji, warto więc śledzić poniższe linki.
Ściągnięcie źródeł + instalacja:
git clone http://llvm.org/git/llvm.git

cd llvm/tools
git clone http://llvm.org/git/clang.git

cd llvm/projects
git clone http://llvm.org/git/compiler-rt.git

cd llvm/projects
git clone http://llvm.org/git/libcxx.git
git clone http://llvm.org/git/libcxxabi.git

cd llvm/projects
git clone http://llvm.org/git/test-suite.git

mkdir llvm_build
mkdir llvm_root
cd llvm_build

cmake -G "Unix Makefiles" DCMAKE_INSTALL_PREFIX=/home/beru/llvm_root/ ../llvm
make -j2
make install
Kompilacja prostego Hello World. Dzięki temu zlepkowi argumentów jestem w stanie używać przekompilowanej przez siebie wersji biblioteki standardowej z przekompilowaną wersją clang-a jak i tą pochodzącą z repozytorium dystrybucji.
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/beru/llvm_root/lib/

~/llvm_root/bin/clang++ main.cpp -std=c++14 -stdlib=libc++ \
    -nodefaultlibs -lc++ -lc++abi -lm -lc -lgcc_s -lgcc \
    -I/home/beru/llvm_root/include/c++/v1/ \
    -L/home/beru/llvm_root/lib/

27 września 2015

[clang] Dynamiczna i statyczna analiza programów

Coś co chciałem wypróbować już bardzo dawno temu, a mianowicie narzędzie do statycznej i dynamicznej analizy kodu/programów. Zachęcił mnie do tego przede wszystkim wykład z GoginNative 2013: The Care and Feeding of C++’s Dragons (0:51h, 1:05h). Najwyraźniej funkcjonalność którą dostarcza kompilator (clang), została wydzielona do osobnego narzędzia jakim jest scan-build, choć nie eksperymentowałem z tym rozwiązaniem za dużo. Kilka przydatnych linków:

Clang Static Analyzer

Narzędzie korzysta ze zbioru reguł, które wciąż się rozrastają. Można skorzystać z domyślnych ustawień lub samodzielnie wskazać pod jakim kątem ma być przeskanowany kod. Przykład z dzieleniem przez zero, zaczerpnięty z dokumentacji.
#include <iostream>

using namespace std;

int test(int z) {
    if (z == 0)
        return 1 / z;
    return -1;
}

int main() {
    int i = 63;
    std::cin >> i;
    test(i);
    return 0;
}
W pierwszych dwóch linijkach dwa alternatywne wywołania:
clang++ -std=c++14 --analyze -Xanalyzer -analyzer-output=text main.cpp
# inna wersja
clang++ -std=c++14 --analyze -Xanalyzer -analyzer-checker=core.DivideZero -Xanalyzer -analyzer-output=text main.cpp
Wynik:
main.cpp:7:18: warning: Division by zero
        return 1 / z;
                 ^
main.cpp:14:10: note: Passing value via 1st parameter 'z'
    test(i);
         ^
main.cpp:14:5: note: Calling 'test'
    test(i);
    ^~~~~~~
main.cpp:6:9: note: Assuming 'z' is equal to 0
    if (z == 0)
        ^~~~~~
main.cpp:6:5: note: Taking true branch
    if (z == 0)
    ^
main.cpp:7:18: note: Division by zero
        return 1 / z;
               ~~^~~
1 warning generated.
Niestety przykłady które stworzyłem samodzielnie, już nie podają takich wyników. Testy przeprowadzałem na wersji 3.6.0 oraz 3.8.0. No nic, rozwiązanie i tak jest interesujące, liczę że w przyszłości narzędzie się rozwinie.
#include <iostream>

using namespace std;

int test2(int z) {
    return 1 / z;
}

int test3(int z) {
    if (z > 15)
        return 1;
    return 1 / z;
}

int main() {
    int i = 63;
    std::cin >> i;

    test2(i);
    test3(i);

    return 0;
}

Sanitizer

Innym narzędziem, które istnieje w clang-u jest sanitizer, a właściwie ich kolekcja. Analiza przeprowadzana jest dynamicznie, więc najlepsze efekty można uzyskać, jeżeli kod był tworzony razem z testami. Sprawdziłem jedynie AddressSanitizer, ale w dokumentacji można odnaleźć informacji o innych:
#include <iostream>

using namespace std;

int main() {
    char tab1[5];
    for(int i = 0; i <= 5; ++i)
        tab1[i] = 'x';
    return 0;
}

Wywołanie:
clang++ -std=c++14 -fsanitize=address main.cpp
./a.out
Rezultat:
=================================================================
==5748==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd47e300b5 at pc 0x0000004dc5ad bp 0x7ffd47e30010 sp 0x7ffd47e30008
WRITE of size 1 at 0x7ffd47e300b5 thread T0
    #0 0x4dc5ac  (/home/beru/synetizer_test/a.out+0x4dc5ac)
    #1 0x7fcefcca4a3f  (/lib/x86_64-linux-gnu/libc.so.6+0x20a3f)
    #2 0x4350e8  (/home/beru/synetizer_test/a.out+0x4350e8)

Address 0x7ffd47e300b5 is located in stack of thread T0 at offset 53 in frame
    #0 0x4dc32f  (/home/beru/synetizer_test/a.out+0x4dc32f)

  This frame has 3 object(s):
    [32, 36) ''
    [48, 53) 'tab1' <== Memory access at offset 53 overflows this variable
    [80, 84) 'i'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
  0x100028fbdfc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbdfd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbdfe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbdff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbe000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100028fbe010: f1 f1 f1 f1 04 f2[05]f2 f2 f2 04 f3 00 00 00 00
  0x100028fbe020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbe030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbe040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbe050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028fbe060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==5748==ABORTING

26 września 2015

Windows Kernel Exploitation - warsztat

Kilka interesujących artykułów, które znalazłem, a dotyczyły one wykorzystanie podatności w jądrze systemu Windows. Punktem wejścia jest tutaj przede wszystkim interakcja z API win32k.sys. Podstawowe funkcje:
  • zawiera windows manager
    • kontroluje wyświetlania okien
    • zarządza wyjściem na ekran
    • kolekcjonuje dane wejściowe z klawiatury, myszy itd.,
    • woła zdefiniowane przez aplikacja hook-i
    • przekazuje mesgi użytkownika do aplikacji
    • zarządza obiektami użytkownika
  • zawiera graphics device interface (GDI) - bibilioteki funkcji graficznych
    • rysowanie i manipulowanie obiektami: liniami, tekstem i figurami
    • zarządza obiektami (GDI) takimi jak brushes, pens, DCs itp.
    • dostarcza API dla sterowników viedeo/drukarki
W sumie po stronie użytkownika można wywołać ponad 800 funkcji. Warto też wiedzieć, że każdy typ obiektu zdefiniowany jest prze unikalną strukturę (np. win32k!tagWND, win32k!tagCURSOR, itp.).

Szczerze mówiąc najbardziej zainteresował mnie warsztat, jakim posługują się autorzy artykułów. Nie kierowałem się żadnym kluczem, po znalezieniu pierwszego artykuły przeskakiwałem do przypisów i tym sposobem stworzyłem sobie małą listę. Nie zagłębiałem się też zbyt mocno w samą tematykę, więc jest to coś do czego będzie warto wrócić.
Ostatnia źródło informacji to bardzo fajna prezentacja z konferencji Blackhat 2011 ("Kernel Attacks Through User- Mode Callbacks") i opowiada o samej architekturze systemu i klasach błędów jakie można w nim napotkać: use-after-free (alokacja i niszczenie obiektów np. znajdujących się w menu), null-pointer-dereference. Więcej około 30-35 minuty.


3 września 2015

[C++11] Muteksy i współdzielenie danych między wątkami

Wstęp

Biblioteka standardowa C++11 wprowadza klasę std::lock_guard typu RAII implementującą idiom muteksu (blokuje muteks w konstruktorze i zwalnia go w destruktorze). Stosując muteksy należy przyswoić sobie dwie ważne zasady:
  1. Nie wolno zwracać lub zapisywać wskaźników lub referencji poza zasięgiem lock-a (blokady).
  2. Należy blokować zasoby tylko przez minimalny okres czasu, potrzebny do wykonania danej operacji.
Przykład poniżej pokazuje wykorzystanie std::lock_guard.
#include <iostream>
#include <string>
#include <thread>
#include <mutex>

using namespace std;

class Storage {
private:
    std::mutex interanl_mutex;
    std::string buff;

public:
    void increment(const std::string id) {
        std::lock_guard<std::mutex> guard(interanl_mutex);
        buff += id;
    }

    void show() {
        std::lock_guard<std::mutex> guard(interanl_mutex);
        std::cout << buff << endl;
    }
};

void update(const std::string id, int loop_counter, Storage& storage) {
    for (int i = 0; i < loop_counter; ++i) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        storage.increment(id);
    }
}

int main() {
    Storage s;
    std::thread t1{update, std::string("a"), 2, std::ref(s)};
    std::thread t2{update, std::string("b"), 3, std::ref(s)};

    t1.join();
    t2.join();

    s.show();
    return 0;
}
Wynik:
ababb

Deadlock - zakleszczenie

Z samym zagadnieniem można zapoznać się tutaj:
Kilka zasad dzięki, którym można uniknąć tego rodzaju problemów:
  1. Nie wolno czekać na inny wątek jeżeli istnieje szansa, że to on może czekać na nas.
  2. Nie wolno zakładać blokad, jeżeli jakaś została już założona. Jeżeli wątek potrzebuje kilku muteksów należy wykonać blokowanie jako pojedynczą operację (stosując np. std::lock)
  3. Jeżeli tworzona jest biblioteka, nie należy wołać kodu użytkownika trzymając jednocześnie blokadę na muteksach. Nigdy nie można być pewnym, czy kod użytkownika też nie założy swojej własnej blokady.
  4. Jeżeli nie można skorzystać z std::lock do założenia kilku blokad jednocześnie, należy pamiętać by blokady zakładać zawsze w tej samej kolejności.
  5. Nie wolno zakładać blokad, jeżeli kod posiada już blokadę na muteksa z niższego poziomu (można stworzyć hierarchical_mutex, który nie jest częścią standardu ale jest łatwy w implementacji [1])
W tym miejscu należy wspomnieć o nowej klasie std::unique_lock, którą można stosować tam gdzie należy odroczyć blokowanie muteksów, albo gdy istnieje potrzeba przetransferowania własności do innego obiektu. Ma ona podobny konstruktor do std::lock_guard.
Drugi parametr konstruktora może przyjmować kilka interesujących parametrów:
  • std::adopt_lock jest używany by zaznaczyć, że muteks został już zablokowany i std::lock_guard albo std::unique_lock powinien tylko zaadoptować własność, zamiast próbować go jeszcze raz blokować.
  • std::defer_lock występuje tylko dla std::unique_lock i mówi o tym że muteks nie powinien zostać zablokowany przez konstruktor. Można tą operację wykonać później wołając np. funkcję std::lock.
Dwa przykłady jak może wyglądać zakładanie kilku blokad jednocześnie:
std::mutex mutex1;
std::mutex mutex2;

std::lock(mutex1, mutex2);
std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
Alternatywa z wykorzystaniem std::unique_lock:
std::mutex mutex1;
std::mutex mutex2;

std::unique_lock<std::mutex> lock1(mutex1, std::defer_lock);
std::unique_lock<std::mutex> lock2(mutex2, std::defer_lock);
std::lock(lock1, lock2);

Inicjowanie zasobów współdzielonych przez wątki

Często zachodzi potrzeba zainicjowania zmiennej tylko przez jeden z wątków. Zamiast samodzielnie tworzyć kod blokujący zasób i sprawdzający czy został on zainicjowany, biblioteka standardowa wprowadza specjalny mechanizm w postaci std::call_flag i std::call_once, które pozwalają na wykonanie takiej operacji (inicjowania) dokładnie raz w bezpieczny sposób.

W C++11 istnieje konieczność stosowania std::ref w przypadku argumentów do funkcji inicjującej, inaczej kompilator zaprotestuje. Zostało to naprawione w C++17.
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>

using namespace std;

std::mutex vec_mutex;
std::once_flag vec_flag;

void show(std::string description, std::vector<int>& vec) {
    std::cout << description;
    for(const auto& v : vec)
        std::cout << v << " ";
    std::cout << std::endl;
}

void init(std::vector<int>& vec) {
    for(auto& v : vec)
        v = 0;
    show("Initialize (call_once):    ", vec);
}

void update(std::vector<int>& vec, int index) {
//  std::call_once(vec_flag, init, std::ref(vec));   // Działa w C++11
    std::call_once(vec_flag, init, vec);             // Działa w C++17
    std::lock_guard<std::mutex> guard(vec_mutex);
    vec[index] += 1;
}

int main() {
    std::vector<int> vec = {9, 9, 9};
    show("Creation (in main thread): ", vec);

    std::thread t1{update, std::ref(vec), 0};
    std::thread t2{update, std::ref(vec), 2};

    t1.join();
    t2.join();

    show("All thread finish:         ", vec);

    return 0;
}
Wynik:
Creation (in main thread): 9 9 9 
Initialize (call_once):    0 0 0 
All thread finish:         1 0 1 

Bibliografia

  • [1] Anthony Williams: C++ Concurency in Action. USA Manning publications Co., 2012. Rozdział 3, str. 33.

2 sierpnia 2015

[python] WinAppDbg

Natchniony bardzo fajnym wykładem Wesley-a McGrew - Instrumenting Point-of-Sale Malware (DEF CON 22), przyjrzałem się bibliotece WinAppDbg.



Biblioteka daje możliwość wygodnego analizowania, debugowania i manipulowania procesami w środowisku Windows. Niestety od wersji 1.5 python 3 nie jest już oficjalnie wspierany, pozostaje więc wersja dla python-a 2. Na stronie głównej projektu, autorzy podają także pokaźny zbiór linków, do podobnych projektów. Najciekawszą rzeczą jest bardzo bogata dokumentacja.
Instalacja za pomocą pip.
pip install winappdbg
I przykładowy program, nic nadzwyczajnego, moja modyfikacja drugiego przykładu z innym/lepszym formatowaniem.
from winappdbg import System

# Create a system snaphot.
system = System()

print('PID:    Exe Name:')
print('-----------------')
for process in system:
    space = ' ' * (8 - len(str(process.get_pid())) - 1)
    print(str(process.get_pid()) + ':' + space + str(process.get_filename()))

26 lipca 2015

[python] V8 JavaScript Engine

V8 to silnik JavaScript, natomiast pyv8 jest jego wrapperem do pythona.
Niestety nie udało mi się znaleźć informacji, w jaki sposób przekompilować wersję dla python-a 3. Z tego co wyczytałem projekt opiera się na boost::python-ie, może okazać się także konieczne zainstalowanie narzędzia scons. Wrapper bazuje na rewizji r19623 i skrypt budujący cofa repozytorium do tej wersji. Jest więc starsze o około 6000 commitów o tego co jest na trunku. Mam nadzieje, że nie oznacza to, że projekt obumiera.
svn checkout http://v8.googlecode.com/svn/trunk/ v8
svn checkout http://pyv8.googlecode.com/svn/trunk/ pyv8

export V8_HOME=~/v8
cp pyv8
python setup.py build

# biblioteka zostanie zapisana w ~/.local
python setup.py install --user 
Przykład:
#! /usr/bin/env python2

import PyV8

ctxt = PyV8.JSContext()
ctxt.enter()
result = ctxt.eval('var str = "Hello world!"; \
                    str.substring(1, 5);')
print(result)
Wynik:
ello

24 lipca 2015

[python] HTTP POST requests - PycURL

Kolejne podejście do wykonania HTTP POST requesta (poprzednie opisałem tutaj). Przydatne linki:
Biblioteka znacznie bardziej popularna i posiadająca wiele portów do różnych języków, co wiąże się z tym, że łatwiej jest odnaleźć przykłady i rozwiązać potencjalne problemy. W rozdziale Quick Start, znajduje się kilka interesujących, z omówieniem ich mechanizmów. Ciekawy wydał mi się pycrul.WRITEDATA. Z tego co udało mi się rozumieć, biblioteka nie posiada bufora na odpowiedź od serwera i to zadaniem aplikacji jest ustawienie go, dzięki tej opcji.
#! /usr/bin/env python3

import pycurl
import urllib.parse
from io import BytesIO

def post3():
    buffer = BytesIO()
    c = pycurl.Curl()

    c.setopt(pycurl.URL, 'http://requestb.in/kcco1337')
    c.setopt(pycurl.WRITEDATA, buffer)

    post_data = {'login': 'user1', 'password': 'hasło 1'}
    post_fields = urllib.parse.urlencode(post_data)
    c.setopt(pycurl.POST, 1)
    c.setopt(pycurl.POSTFIELDS, post_fields)

    c.perform()
    c.close()

    print(buffer.getvalue().decode('iso-8859-1'))
Raw body:
password=has%C5%82o+1&login=user1

23 lipca 2015

Budowanie aplikacji NaCl (NativeClient) w przeglądarce

Podjąłem próbę zbudowania przykładowej aplikacji NaCl w przeglądarce, ale muszę się przyznać, że odniosłem porażkę. Może w przyszłości jeszcze się tym zajmę. Do pracy zachęcił mnie filmik:


Ale w kilku miejscach jest on nieaktualny, dlatego warto równolegle śledzić odświeżoną wersję dokumentacji:
Kilka problemów, które mi się przytrafiły.
  • Przy pierwszym odpaleniu, konsola zawisała podczas ściągania pakietów. Pomogło zamknięci i ponowne odpalenie, za każdym razem kolejne pakiety się instalowały i konsola zawisała na jakimś następnym. Trzeba było kontynuować, dopóki nie pokazał się prompt bash-a.
  • Uruchomienie programu natychmiast zamykało konsolę. Rozwiązaniem okazało się wyłącznie "Native Client GDB-based debugging".
  • Po odpaleniu dema, aplikacja wyświetlała w przeglądarce status: "Status: ERROR [NaCl module load failed: Nexe crashed during startup]". Niestety tego nie udało mi się już pokonać.

22 lipca 2015

[gdb] - pomocne instrukcje

Współpraca z gdb zaczyna się rozwijać, więc mała notka z najbardziej przydatnymi komendami, ale najpierw przydatne linki:
Te klika komend warto wpisać na stałe do pliku konfiguracyjnego ~/.gdbinit.
# zastąpienie domyślnego assemblera att, na wersję intel-owsą
set disassembly-flavor intel

# zapisywanie historii poleceń do pliku
set history filename ~/.gdb_history
set history save on
set history size 100000

# TUI - text user interface (po wywołaniu layout)
set tui border-kind ascii
set tui border-mode half
set tui active-border-mode bold
Komendy TUI, które pozwalają zorientować się co tak naprawdę dzieje się z programem:
  • layout - wyświetla pseudo okno, w którym może być prezentowany kod assemblera, rejestry lub kod źródłowy. Przykładowe wywołania: layout asm, layout src, layout reg
  • info win - informacja o wyświetlanych oknach
  • focus - przełączanie focusu na różne okna, przydatne przy pracy z myszką, przykład: focus cmd
  • refresh - odświeża wszystkie okna, sam nie wiem czemu, ale ich wygląd często się psuje. Wygodniejszą metodą służącą odświeżaniu jest zastosowanie skrótu klawiaturowego, ale o tym poniżej
Przydatne skróty klawiaturowe w pracy z TUI.
  • Wyłączanie TUI, można zastosować jeden z poniższych skrótów:
    • Ctrl+x Ctrl+a
    • Ctrl+x a
  • Odświeżanie okien - odpowiednik komendy refresh:
    • Ctrl+x+l
  • Włączanie layoutu
    • Ctrl+x 1 - jedno okno
    • Ctrl+x 2 - dwa okno

  • Szybkie odwołanie się do komend z historii, bez potrzeby fokusownia okna cmd i używania strzałek
    • Ctrl+p/Ctrp+w - góra/dół

Operowanie z kodem, to przede wszystkim prezentacja wycinków pamięci w jakimś przyjaznym formacie:
  • display - wyświetla zmienną/rejestr, przy każdym zatrzymaniu programu, np. display $eax
  • x - wyświetlanie zwartości pamięci, format wywołania wygląda następująco
    x/[rozmiar][format][rozmiar pola] [adres]
    , gdzie:
    • Rozmiar - ilość bajtów jakie będą wyświetlone
    • Format:
      • d - dec
      • x - hex
      • z - jakeiś inne hex?
      • u - unsigned dec
      • f - float
      • c - char (ASCII)
      • a - adres
      • i - instrukcja
      • s - string
    • Rozmiar pola:
      • b - byte (8 bitów)
      • h - halfword (16 bitów)
      • w - word (32 bity)
      • g - giant (64 bity)
Przykład użycia x:
(gdb) x/12xb 0x8048af4
0x8048af4 <_ZN8MyClass1C2Ev>:    0x55  0x89  0xe5  0x83  0xec  0x08  0x8b  0x45
0x8048afc <_ZN8MyClass1C2Ev+8>:  0x08  0x83  0xec  0x0c
Debugowani kodu assemblera trochę różni się trochę od sytuacji, gdy działamy tylko z kodem źródłowym. W takim wypadku zamiast wyniku działania jednej linii kodu, interesuje nas bardziej jaki jest wynik działania jednej instrukcji kodu:
  • si - wykonuje pojedynczą instrukcję

21 lipca 2015

Budowanie wyrażeń regularnych z regex101.com

Super strona ułatwiająca budowanie i debugowanie wyrażeń regularnych. Nie znam nic co można by porównać do możliwości tej aplikacją, z tak przyjaznym interfejsem.

13 lipca 2015

Boost.Python - Pisanie rozszerzeń dla python-a w C++

Boost.Python pozwala na wykorzystanie klas i metod zaimplementowanych w C++ w Pythonie i odwrotnie. Skupiłem się na pierwszej części. Garść przydatnych linków:
Takie mieszanie technologi, niestety sprawia, że szukanie błędów staje się trochę denerwujące, ale już po dwóch godzinach udało mi się uzyskać działający przykład, który mniej więcej jest tym co chciałem uzyskać. Co ciekawe (jak mówi dokumentacji), kod napisany w C++, rzadko będzie wymagał zmian dostosowujących go do współpracy z Boost.Python-em, co uważam jest dużym plusem.
W pierwszej kolejności należy stworzyć plik źródłowy (tutaj SimplePrinter.cpp), z deklaracją wrappera - gdzie mówimy jakie interfejsy będą widoczny w Pythonie. Boost.Python posiada abstrakcję na std::vector, z której tutaj skorzystałem. Później korzystając z takiego modułu, należy stworzyć specjalny obiekt, dzięki któremu można skonwertować np. listę pythonową i skorzystać z danych w niej zawartych w kodzie C++ (przykład na końcu). Kilka ważnych uwag.
  • Należy pamiętać aby nazwa modułu (w BOOST_PYTHON_MODULE) była identyczna z nazwą pliku biblioteki dynamicznej, inaczej podczas importowanie takiego modułu można natknąć się niewiele mówiący błąd.
  • Nazywanie metody "print" sprawiła, że uzyskiwałem błąd, gdy próbowałem z takiej metody skorzystać. Najpewniej jest to związane z jakimś konfliktem, gdyż print (Python 2) jest słowem kluczowym.
#include <iostream>
#include <string>
#include <vector>

struct SimplePrinter
{
    void setWords(std::vector<std::string> words) {
        _words = words;
    }

    size_t count() const {
        return _words.size();
    }

    void show() {
        std::cout << "All: ";
        for (const auto& w : _words) {
            std::cout << w << " ";
        }
        std::cout << std::endl;
    }

private:
    std::vector<std::string> _words;
};

#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
using namespace boost::python;

BOOST_PYTHON_MODULE(simple_printer)
{
    class_<std::vector<std::string>>("VectorOfStrings")
        .def(vector_indexing_suite<std::vector<std::string>>());

    class_<SimplePrinter>("SimplePrinter")
        .def("setWords", &SimplePrinter::setWords)
        .def("count", &SimplePrinter::count)
        .def("show", &SimplePrinter::show);
}
W celu skompilowania przykładu skorzystałem z Pythonowego distutils, które umożliwia budowanie modułów (nie miałem z tym narzędziem wcześniej do czynienia, ale wydaje się bardzo schludnym rozwiązaniem). W pierwszej kolejności należy stworzyć plik setup.py (w moim przypadku w tym samym katalogu, gdzie znajdowały się źródła). Tutaj ponownie, trzeba sprawdzić, czy nazwa modułu zgadza się z tą, która została zadeklarowana w wraperze C++. Co ważne, moja klasa korzysta z nowego standardu C++14, stąd dodatkowa flaga: extra_compile_args.
#!/usr/bin/env python

from distutils.core import setup
from distutils.extension import Extension

setup(name="PackageName",
    ext_modules=[
        Extension(name="simple_printer",
            sources=["SimplePrinter.cpp"],
            libraries=["boost_python"],
            extra_compile_args=["-std=c++14"])
    ])
Budowanie:
$ cd cpp_boost_python
$ ls
setup.py  SimplePrinter.cpp

$ python setup.py build

running build
running build_ext
building 'simple_printer' extension
creating build
creating build/temp.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c SimplePrinter.cpp -o build/temp.linux-x86_64-2.7/SimplePrinter.o -std=c++14
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
creating build/lib.linux-x86_64-2.7
c++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wl,-Bsymbolic-functions -Wl,-z,relro -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/SimplePrinter.o -lboost_python -o build/lib.linux-x86_64-2.7/simple_printer.so
Po zbudowaniu biblioteka będzie znajdować się w podkatalogu build (build/lib.linux-x86_64-2.7/simple_printer.so).
Na sam koniec test działania.
$ cd build/lib.linux-x86_64-2.7
$ ls 
simple_printer.so

$ python
>>> import simple_printer
>>> s = simple_printer.SimplePrinter()
>>> words = simple_printer.VectorOfStrings()
>>> words.extend(w for w in ["first", "second"])
>>> s.setWords(words)
>>> s.count()
2
>>> s.show()
All: first second

30 maja 2015

[C++11] Przekazywanie zmiennych do wątku

Najpierw kilka przydatnych metod i obiektów do eksperymentowania z std::thread i std::this_thread.
  • hardware_concurrency - informuje ile wątków ze wsparciem sprzętowym jest dostępnych w systemie. Jeżeli planujemy zrównoleglić naszą pracę, trzeba pamiętać, że jeden wątek jest już zajęty przez nasz program.
  • get_id - metoda wołana z std::this_thread albo na obiekcie wątku. Pozwala zorientować się z jakim wątkiem mamy do czynienia. Standard nie definiuje co to będzie, ale gwarantuje, że taki obiekt (std::thread::id), może być porównywany i wyświetlany na standardowym wyjściu.
  • sleep_for - pozwala uśpić wątek na pewien czas.

Konstruktor std::thread ma podobną konstrukcję do std::bind. Trzeba pamiętać, że domyślnie wszystkie przekazane zmienne są kopiowane. Gdy interesuje nas aby wątek wyliczył i zapisał jakąś wartość do zmiennej przekazanej przez referencję, należy skorzystać z std::ref (tak jak w przypadku std::bind).
#include <iostream>
#include <thread>

using namespace std;

struct Presenter {
    void operator()(int& value) {
        cout << "Slave id: " << std::this_thread::get_id() << endl;
        value += 100;

        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
};

int main() {
    cout << "Possible threads: " << std::thread::hardware_concurrency() << endl;
    cout << "Master id: " << std::this_thread::get_id() << endl;

    int value = 1;

    std::thread t{ Presenter(), std::ref(value) };
    cout << "Child id from master: " << t.get_id() << endl;

    t.join();
    return 0;
}
Wynik:
Possible threads: 4
Master id: 140557572298560
Child id from master: 140557555422976
Slave id: 140557555422976
Kłopotliwe mogą okazać się wszelkie niejawne rzutowania. W przykładzie poniżej, funkcja fun() przyjmuje jako argument std::string. Wykonanie programu będzie przebiegało w ten sposób. W pierwszej kolejności do wątku przekazany zostanie wskaźnik (char const*), a następnie wykonana zostanie konwersja do std::string (już w kontekście nowego wątku). Istnieje bardzo duża szansa, że gdy konwersja się zadzieje, zmienna z głównego wątku nie będzie już istnieć, a działanie programu będzie trudne do przewidzenia. Rozwiązaniem jest wykonanie jawnego rzutowania przed przekazaniem zmiennych do std::thread.
#include <iostream>
#include <string>
#include <thread>

using namespace std;

void fun(std::string s) {
    cout << "Received: " << s << endl;
}

int main() {
    char buf[10];
    for (int i = 0 ; i < 3; i++) {
        std::sprintf(buf, "%d", i);
        cout << "Setting: " << buf << endl;

        std::thread t{ fun, buf };
//      std::thread t{ fun, std::string(buf) };    // OK!

        t.detach();
    }
    std::sprintf(buf, "%d", 77);
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
Wynik. Jeden z wątków dokonuje konwersji z char* na std::string, gdy wątek główny zmienił już zawartość bufora na 77 (linijka 22).
Setting: 0
Setting: 1
Received: 1
Received: 2
Setting: 2
Received: 77

19 maja 2015

[C++] Aggregate, trivial classes, standard-layout

Nowy standard wprowadza kilka definicji, które trzeba przyswoić.

Aggregate


Agregaty (tablice lub klasy), które mogą być inicjalizowane nawiasami klamrowymi (nie ma to nic wspólnego z std::initializer_list).
Jeżeli elementów, które podamy w klamrach będzie mniej, niż tych zadeklarowanych, to dla brakujących elementów zostanie (o ile to możliwe) zawołany konstruktor domyślny.
Agragaty, nie mogą mieć:
  • konstruktora stworzonego przez użytkownika (ale user-declared jest w porządku)
  • niestatycznych składowych zainicjowanych w miejscu deklaracji
  • prywatnych lub chronionych niestatycznych pól składowych
  • klas bazowych
  • metod wirtualnych
#include <iostream>
#include <string>
#include <vector>

struct AggregateClass {
//  AggregateClass() {}           // ERROR - stworzone przez użytkownika
    AggregateClass() = default;   // OK - "user-declared"

    int a;
    std::string b;
    std::vector<double> c;
};

int main() {
    AggregateClass ag = {3, "text", {1.41, 3.14}};
}

Trivial classes


Klasy trywialne wspierają statyczną inicjalizację typu. Klasa trywialna to klasa, która ma trywialny konstruktor domyślny i jest trywialnie kopiowalna.
Klasa trywialnie kopiowalna (trivially copyable), to taka, której instancje mogą być kopiowane bitowo np. przy pomocy std::memcpy().

W opisie metod traitsów, są szczegółowe wymagania:
#include <iostream>
#include <type_traits>

struct Trivial {
    Trivial() = default;

    int value;
};

int main() {
    std::cout << std::boolalpha;

    std::cout << std::is_trivial<Trivial>::value << "\n";
    std::cout << std::is_trivially_copyable<Trivial>::value << "\n";
    std::cout << std::is_trivially_default_constructible<Trivial>::value << "\n";
}
Wynik:
true
true
true

Standard-layout


Typy standard-layout mają takie same ułożenie pól w pamięci jak struktury w języku C. Warunki, które muszą spełniać

POD - przestarzałe od C++20


Typy POD, są kompatybilne bitowo ze strukturami języka C. W nowej wersji standardu wprowadzono nową (uszczegółowioną) definicję typów POD (plain old data). Typem POD jest klasa, która jest jednocześnie trivial classes i standard-layout i jest to właściwość spełniona (rekursywnie) dla wszystkich jej pól.
W C++20 std::is_pos zostało usunięte.

Przykład dla POD i standard-layout

#include <iostream>
#include <type_traits>

struct A {
    int field;
};

struct B {
    int field1;
private:
    int field2;
};

int main()
{
    std::cout << std::boolalpha;

    std::cout << "A trivial: " << std::is_trivial<A>::value << "\n";
    std::cout << "A standard_layout: " << std::is_standard_layout<A>::value << "\n";
    std::cout << "A pod: " <<std::is_pod<A>::value << "\n";

    std::cout << "\n";
    std::cout << "B trivial: " << std::is_trivial<B>::value << "\n";
    std::cout << "B standard_layout: " << std::is_standard_layout<B>::value << "\n";
    std::cout << "B pod: " << std::is_pod<B>::value << "\n";
}
Wynik:
A trivial: true
A standard_layout: true
A pod: true

B trivial: true
B standard_layout: false
B pod: false

14 maja 2015

[C++] std::thread is low level primitive

Na obiektach std::thread zawsze należy wołać join() lub detach(), a brak decyzji skutkuje poważnym błędem. Operacje tworzenia obiektu i "decyzja" o jego losie są rozłączne, co niechybnie zachęca do popełniania pomyłek. Większość książkowych przykładów zachęca do pisania własnych wraperów typu RAII, ale dlaczego sam standard nie oferuje czegoś podobnego? Odpowiedź przyniósł bardzo fajny wpis na blogu Andrzeja Krzemieńskigo.
std::thread należy rozpatrywać jako nisko poziomowy (low level) prymityw. std::thread jest tylko budulcem do tworzenia wysoko pozimowych rozwiązań, tak jak delete, jest budulcem, dzięki któremu może istnieć std::unique_ptr. W momencie wprowadzania tego rozwiązania komitetowi standaryzacyjnemu po prostu zabrakło czasu, na coś więcej. Tak jak należy wystrzegać się delete, w produkcyjnym kodzie, tak samo powinno być traktowane std::thread. Być może boost::strict_scoped_thread będzie dobrym rozwiązaniem.
#include <iostream>
#include <thread>
#include <boost/thread/scoped_thread.hpp>

void f()
{
    std::cout << "Hello" << std::endl;
}

int main()
{
    std::thread t1(f);
    t1.join();

    boost::strict_scoped_thread<> t2((boost::thread(f)));  // podwójne nawiasy

    return 0;
}
I jeszcze konfiguracja CMakeLists.txt
project(cpp_thread)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

find_package(Boost COMPONENTS system thread REQUIRED)
target_link_libraries(${PROJECT_NAME}
    pthread
    ${Boost_SYSTEM_LIBRARY}
    ${Boost_THREAD_LIBRARY}
)

4 maja 2015

Vim - ściąga

Robię wszystko, aby nie musieć korzystać z narzędzia jakim jest vim, ale często jest to jedyna alternatywa, do wystukiwania echo na konsoli. Kilka linków do opisów, ułatwiających pracę z tym narzędziem.

2 maja 2015

[C++Now!] Michael Caisse: Introduction to Modern C++ Techniques

Dwa ciekawe wykłady, na temat nowoczesnych technik stosowanych w C++ (choć z 2012 roku). To co mnie zainteresowało (a szczególnie ostatni punkt):
  • Traits
  • Policy Classes
  • CRTP (Curiously Recurring Template Pattern)
  • SFINAE (Substitution Failure is Not An Error)
  • Units - jak radzić sobie z jednostkami fizycznymi



11 kwietnia 2015

[C++11] Aliasy na szablony z wykorzystaniem using

W nowym standardzie using zyskał nowe właściwości pozwalające na tworzenie aliasów do typów. Parametrem takiego aliasu, może być także stała znana w czasie kompilacji (np. rozmiar dla std::array). Nie ma możliwości tworzenia specjalizacji.
#include <iostream>
#include <map>

template <typename T>
using CourseGrade = std::map<std::string, T>;

int main()
{
    CourseGrade<double> mathCourse;

    mathCourse["Jhon"] = 3.5;
    return 0;
}

4 kwietnia 2015

Ataki na z/OS

Czytając artykuł na ZaufanejTrzciejStronie,
przypomniał mi się wykład na temat ataków na platformę z/OS z DEF CON 2014.



Pieniądze w bankach, znów nie wyglądają na takie bezpieczne...

[PowerShell] Wyszukiwanie plików ukrytych

Gdyby zaszła potrzeba znalezienia ukrytego pliku, konfigurującego wirusa rządowego:
Wydaj mi się, że PowerShell jest całkiem potężnym narzędziem, ale gdy chce rozwiązać nim jakiś problem zawsze napotykam na jego ograniczenia. Właściwe wyszukiwanie
  • -Force - zwraca wszystkie pliki w folderze, nawet te ukryte
  • -ErrorAction SilentlyContinue - niektóre zasoby jak np. "c:\Users\beru\Documents\My Music" występują jako jakaś windowsowa forma dowiązania (junction). Ta opcja ukrywa las błędów, który jest generowany przez nieudane próby wejścia. Plik z "My Music" są umieszczone w "c:\Users\beru\Music" i ten folder zostanie przeszukany
# Pomoc
Get-Help Get-ChildItem
Get-Help Get-ChildItem -Detailed

# Sprawdzenie atrybuty pliku
(Get-ItemProperty .\Music\f14.conf).attributes.ToString() -match "Hidden"

# Wyszukiwanie
Get-ChildItem . -Force -Recurse -ErrorAction SilentlyContinue | Where-Object {$_.mode -match "h" -and $_.name -eq "f14.conf"}

28 lutego 2015

[boost] Sprawdzenie typu za pomocą typeindex

Od wersji 1.56 boost, posiada fajną bibliotekę, dostarczająca informacje o typie zmiennej (boost_typeindex). Podobno radzi sobie lepiej, niż mechanizmy typeid() albo std::type_index (C++11). Z pewnością wyniki są bardziej czytelne dla człowieka.

W Ubuntu 14.10 dostępny jest tylko boost w wersji 1.55, więc trzeba ściągnąć źródła samodzielnie:
wget -c http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.gz/download
mv download boost_1_57.tar.gz
tar -zxv boost_1_57.tar.gz
Podczas kompilacji trzeba jeszcze wskazać gdzie są nasze pliki nagłówkowe:
g++ -std=c++14 -I/home/beru/boost_1_57_0/ main.cpp
A teraz testowanie. auto w trakcie dedukcji typu usuwa modyfikatory const i volatile.
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>

using namespace std;

int main()
{
    const vector<int> val1;
    auto  a = val1;
    auto& b = val1;

    int val2 = 44;
    const int* p = &val2;
    auto c = p;

    cout << "For a: " << boost::typeindex::type_id_with_cvr<decltype(a)>().pretty_name() << endl;
    cout << "For b: " << boost::typeindex::type_id_with_cvr<decltype(b)>().pretty_name() << endl;
    cout << "For c: " << boost::typeindex::type_id_with_cvr<decltype(c)>().pretty_name() << endl;

    return 0;
}
Wynik:
For a: std::vector<int, std::allocator<int> >
For b: std::vector<int, std::allocator<int> > const&
For c: int const*

18 lutego 2015

wget - utworzenie sesji i archiwizacja strony

Rozwiązanie problemu archiwizacji strony, do której konieczne jest logowanie. Po zalogowaniu tworzona jest sesja, którą można wykorzystać do ściągnięcia zawartości strony:
wget --save-cookies cookies.txt \    # zapisuje ciasteczko
     --keep-session-cookies \        # ciasteczka sesji normalnie nie są zapisywane, ta opcja to wymusza
     --post-data 'user=Alice&pass=123' \ # logowanie na stronie (metoda POST)
     http://example.com

wget --recursive \                   # przeszukaj stronę rekursywnie
     --cookies=on \                  # man nie opisuje, wget nie odrzuca, niektóre przykłady podają (?)
     --load-cookies cookies.txt \    # ładuje wcześniej zapisane cookie
     --keep-session-cookies \        # nadal utrzymuje cookie sesji 
     --no-clobber \                  # nie nadpisuj już istniejących plików
     --page-requisites \             # ściągnij wszystkie pliki (pliki dźwiękowe, css, itd.)
     --html-extension \              # zapisz pliki z rozszerzeniem html
     --restrict-file-names=windows \ # eskejpuje znaki by Windows (są też inne tryby) sobie poradził
     --domains example.com \         # nie śledź linków poza domenom
     http://example.com
Podzieliłem problem na dwie części, ale pewnie da się to zamknąć do jednego polecenia.

17 lutego 2015

Parser na bazie własnej gramatyki - pyparsing

Czasami zachodzi potrzeba, przeanalizowania danych, na które nie ma dedykowanego parsera. Niekiedy wystarcza regexp, ale jeżeli struktury danych zaczynają być zagnieżdżone, można pokusić się o stworzenie gramatyki i skorzystanie z jakiejś biblioteki. Ponieważ dawno się już tym nie zajmowałem, nie przywiązywałem szczególnej uwagi, czy biblioteka pozwala na zapis zbliżony do notacji BNF (http://pl.wikipedia.org/wiki/Notacja_BNF).

Wybrałem pierwszą z brzegu, omówioną w wykładzie Erik Rose - "Parsing Horrible Things with Python":



Dokumentacja jest moim zdaniem kiepska (pierwszy link), ale następny link (wykład Paula McGuire) bardzo ułatwiły pracę:
Instalacja pyparsing w wersji 2.0.3.
pip install pyparsing
Przykład - wyciąganie liczby ze stringu, który może być wielokrotnie zamknięty za pomocą nawiasów < i >
import pyparsing as pp

startMark = pp.Suppress(pp.Literal('<'))
endMark = pp.Suppress(pp.Literal('>'))

greet = pp.Word(pp.alphas + ',')
value = pp.Word(pp.nums)
lastContent = greet + value

expr = pp.Forward()

content = pp.Group(expr).setResultsName('Indent') | \
          pp.Group(lastContent).setResultsName('Right Content')

expr << startMark + content + endMark

grammar = expr

data = '<<Hello, 1337>>'
res = grammar.parseString(data)
print(res)
print(type(res))
print(res[0].getName())
Wynik:
[[['Hello,', '1337']]]
<class 'pyparsing.ParseResults'>
Indent
Do budowania gramatyk, najlepiej korzystać z operatorów +, ^, |. Do dopasowania tekstu służy nam wiele obiektów (tokenów) np.
  • Word - dopasowuje tekst z dowolnego złożenia znaków
  • Literal - dopasowuje zwartość do znaków dokładnie w takiej kolejności jak zostały podane
  • Suppress - dopasowuje, ale zawartość zostanie pominięta w wyniku
Problemem może być dopasowywanie do zagnieżdżonych zawartości. Token w takim przypadku, powinien odwoływać się sam do siebie. Z pomocą przychodzi Forward(), a ostateczny opis należy wykonać za pomocą operatora << (linijka 14).
Group, pozwala na grupowanie wyników/tokenów jakie pojawią się w ostatecznym wyniku.
Słowo jeszcze o metodach, które okazały się w moim przypadku niezwykle przydane:
  • delimitedList(expression, delim=',') - dopasuje wyrażenie, rozdzielone za pomocą delimitera (domyślnie jest to przecinek)
  • Group.setResultsName() - nadaje nazwę do dopasowanego tokenu, ma niby posłużyć w celu zbudowania słownika ze sparsowanymi danymi, ale ja po prostu korzystam z getName() na wynikowych danych, gdy po nich iteruje
  • ParseResults.asList() - wynik domyślnie jest obiektem ParseResult, ale możemy skorzystać z konwersji i przekształcić go na listę