Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Wednesday, April 15, 2020

HawkTracer - low-overhead instrumentation-based profiler


Disclaimer: this post introduces HawkTracer profiler but doesn't try to go deep into details of that. Follow the links attached to find all the features (and limitations) of the tool.

A while ago, at Amazon we've open-sourced instrumentation-based profiler - HawkTracer - which introduces very low overhead so it can be used on low-end platforms, where development environment is somehow limited (e.g. no ssh access, very limited disk storage etc). We used it to fix performance issues of Prime Video app on living room devices (SmartTVs, Streaming sticks, Game consoles etc).

The profiler can be used to measure resource usage (mainly time spend in scope, but also others like CPU usage etc) in your C/C++ code (but it's easy to write bindings for other languages, there are already some in Python and Rust) and then visualize it using Trace Event Profiling Tool or as FlameGraphs. Example instrumentation can be found below:

void foo()
{
  HT_G_TRACE_FUNCTION();

  very_expensive_call();
}
void bar()
{
  HT_G_TRACE_FUNCTION();
  for (int i = 0; i < 100; i++)
  {
    foo();
    {
      HT_G_TRACE_OPT_STATIC("InternalOp");
      recursive(10);
    }
  }
}

That example generates following results:

How does it work?

The idea behind HawkTracer is very trivial and well-known - the design is a simple server-client architecture, where the profiling application (usually running on an embedded device) runs as a server that emits tracing events. The serialized events are transmitted to a client through an user-specific protocol (by default TCP/IP and File are supported, but that can be extended by user). The client then can convert received events to some human-readable format - again, only few of them are supported by default (ChromeTracing and FlameGraph) but user can extend the client by adding more conversion methods.
The HawkTracer itself is a library that's just linked to your executable, so there's no need to run separate profiling process.



But it's C... and we all need Rust now!

Well, if you're a Rust lover, I have a good news for you. There are already bindings for HawkTracer in Rust! The snippet below shows how can you instrument your codebase:

#[macro_use]
extern crate rust_hawktracer;
use rust_hawktracer::*;
use std::{thread, time};

#[hawktracer(trace_this)]
fn method_to_trace() {
    thread::sleep(time::Duration::from_millis(1));
}

fn main() {
    let instance = HawktracerInstance::new();
    let _listener = instance.create_listener(HawktracerListenerType::TCP {
        port: 12345,
        buffer_size: 4096,
    });

    println!("Hello, world!");
    {
        scoped_tracepoint!(_test);
        thread::sleep(time::Duration::from_millis(10));

        {
            for _ in 0..10 {
                scoped_tracepoint!(_second_tracepoint);
                thread::sleep(time::Duration::from_millis(10));
            }
        }
    }
}

The tracepoints can be disabled at build time (as they're implemented as macros) - see the repository for details.

Want to know more?

This blogpost is just a very short introduction to HawkTracer. I'll be posting here more about the profiler, but in the meantime, checkout the following links to have more details about it:

Friday, April 7, 2017

GNOME Paint - simple drawing app for GNOME

TL;DR
I've started working on simple drawing application for GNOME. Current state - just started (see the current screenshot [1]), but progressing. Help needed (especially UX guys).

Motivation
A couple of weeks ago I've needed to make a very simple modification of the image - some cutting, moving some part of the image from left to right, draw a few lines. Turns out, that I couldn't easily find a software, that I could use. I've tried following:
  • gnome-paint [2] - the name (GNOME-XXX) convinced me to try this tool as a first one. The first attempt of modifying the image (AFAIR I just wanted to resize the canvas) - crash. Ok, that happens. Fortunately, we live in the open source world so I can fix it. I've downloaded the sources, and... bang! The project was unmaintained for a while (last commit more than 6 years ago), GTK+2, etc - I gave up. I didn't have that much time, I just needed to do simple modifications of the image. So let's try another tool:
  • gpaint [3] - the same story - unmaintained, and crashed after few clicks. Keep searching, and I found:
  • GNU Paint [4] - this app looks awful (especially if you got used to beautiful GNOME apps) but at least it seems functional. Unfortunately, didn't work for me neither. After few minutes of using it, the app just didn't respond to the input (yes, it means that I couldn't save my work, I had to do the print-screen...). And finally, I've installed:
  • KolourPaint [5] with tons of KDE stuff... seriously, I couldn't believe that I can't easily find simple image editor for GNOME...
Hey, but what about GIMP?
Sorry to say that, but it's not a SIMPLE image editor. Every time when I tried to do something that would be simple in MS Paint, I had to search for a tutorial. Every single time. 

You didn't do the research good enough, I know tool XXX which is based on GTK+
That's true, I didn't spend that much time on doing the research. However, after a few failures, I was just tired of this. I just wanted to do a simple modification, so I finally ended up with an app that I used to use some time ago, which was stable, functional, and simple. Now I'm aware of a few more apps (Pinta, mtpaint etc).

GNOME Paint
I've decided to start a new project - GNOME Paint. You can find some of the reasons in the paragraph "Motivation", here are the others:
  1. There is no GNOME-look-like drawing editor. Would be nice to have one. If the app will be good enough, and people will like it, it might be a part of GNOME core apps (but that's very long way)
  2. I finally want to dive deep into the GTK+ framework. I used to use it for several projects (mostly GTKMM), but without deep understanding of the framework, and working on a real project is IMO a better way for learning the framework than just reading the manual.
  3. I'm going to use it a lot, and it's also fun using software that you made on your own.
Current state
See the screenshot below. I've high-level design and some basic data structures, but still missing minimal functionality, so it's not even close to the first release. Also, the UI needs to be re-done (but I didn't pay attention to the UI so far). Hopefully, I'll be able to do a simple demo of the first release during the lightning session at GUADEC, but no promises.

Help needed
Any help (most important - UX, GNOME HIG experts) very welcome!


Links

Saturday, July 16, 2016

Generic C++ GObject signals wrapper

Recently I've discovered, that connecting to signals in gstreamermm can be really inconvenient. The problem doesn't exist in the other mm libraries, because most of the classes and their signals are wrapped.
But GStreamer allows to create user-defined elements, so it's actually impossible to wrap everything in gstreamermm (for now library supports wrappers for gstreamer plugins core and base).

Currently, if you want to connect to a signal in gstreamermm, you have two options:
  1. Using pure C API:

  2. auto typefind = Gst::ElementFactory::create_element("typefind");
    
    g_signal_connect (typefind->gobj(), 
                      "have-type", 
                      G_CALLBACK(cb_typefind_havetype), 
                     (gpointer *)typefind->gobj());
    
    static void cb_typefind_havetype (GstTypeFindElement *typefind,
                                      guint               probability,
                                      GstCaps            *caps,
                                      gpointer            user_data)
    {
      // callback implementation
    }
    Well, it's not very bad. But... you have to use C structures in the callback instead of C++ wrappers.

  3. Using gstreamermm API
  4. As I mentioned, gstreamermm provide wrappers for core and base plugins, so some of the elements (and their signals) are already wrapped in the library:
    auto typefind = Gst::TypeFindElement::create();
    typefind->signal_have_type().connect(
        [] (guint probability, const Glib::RefPtr& caps)
        {
          // callback implementation
        });
    
However, many plugins are not wrapped (and they never will), so usually you need to either write wrapper for element which you wanted to use (and then, maintain this wrapper as well), or use pure C API.
Moreover, I'm going to remove plugin API in the next release [1], so user won't be able to use gstreamermm API even for the base and core plugins. I was wondering, if it would be possible to write generic wrapper for the GObject signals. So... there you are! The solution is not perfect yet, and I haven't tested it so much, but so far it works fine with few plugins and signals.

namespace Glib
{  
  template <typename T>
  static constexpr T wrap (T v, bool=true)
  {
    return v;
  }

  template <typename T>
  static constexpr T unwrap (T v, bool=true)
  {
    return v;
  }

  template<typename T>
  using unwrapped_t = decltype(unwrap(*((typename std::remove_reference<T>::type*)nullptr)));

  template<typename T>
  constexpr T return_helper()
  {
    typedef unwrapped_t<T> Ret;
    return Ret();
  }

  template<>
  constexpr void return_helper()
  {
    return void();
  }
}


template<typename>
class signal_callback;

template<typename Ret, typename ...T>
class signal_callback<Ret(T...)>
{
  template<typename ...Args>
  static auto callback(void* self, Args ...v)
  {
    using Glib::wrap;
    typedef sigc::slot< void, decltype(wrap(v))... > SlotType;

    void* data = std::get<sizeof...(Args)-1>(std::tuple<Args...>(v...));

    // Do not try to call a signal on a disassociated wrapper.
    if(dynamic_cast<Glib::Object*>(Glib::ObjectBase::_get_current_wrapper((GObject*) self)))
      {
	try
	  {
	    if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
	      {
		(*static_cast<SlotType*>(slot))(wrap(std::forward<Args>(v), true)...);
	      }
	  }
	catch(...)
	  {
	    Glib::exception_handlers_invoke();
	  }
      }

    return Glib::return_helper<Ret>();
  }

public:
  auto operator()(const std::string& signal_name, const Glib::RefPtr<Glib::Object>& obj)
  {
    using Glib::unwrap;
    static std::map<std::pair<GType, std::string>, Glib::SignalProxyInfo> signal_infos;

    auto key = std::make_pair(G_TYPE_FROM_INSTANCE (obj->gobj()), signal_name);
    if (signal_infos.find(key) == signal_infos.end())
      {
	signal_infos[key] = {
	  signal_name.c_str(),
	  (GCallback) &callback<Glib::unwrapped_t<T>..., void*>,
	  (GCallback) &callback<Glib::unwrapped_t<T>..., void*>
	};
      }

    return Glib::SignalProxy<Ret, T... >(obj.operator->(), &signal_infos[key]);
  }
};


auto typefind = Gst::ElementFactory::create_element("typefind"),
signal_callback<void(guint, const Glib::RefPtr<Gst::Caps>&)> signal_wrapper;

signal_wrapper("have-type", typefind).connect(
  [&ready, &cv] (guint probability, const Glib::RefPtr<Gst::Caps>& caps) {
    std::cout << "have-type: probability=" << probability << std::endl;
    Gst::Structure structure = caps->get_structure(0);
    const Glib::ustring mime_type = structure.get_name();
    std::cout << "have-type: mime_type=" << mime_type << std::endl;

    structure.foreach([] (const Glib::QueryQuark& id, const Glib::ValueBase& value) {
      const Glib::ustring str_id = id;
      gchar* str_value = g_strdup_value_contents(value.gobj());
      std::cout << "Structure field: id=" << str_id << ", value=" << str_value << std::endl;
      g_free(str_value);		      
      return true;
  });
});

Full source of the code can be found on the github [2].
As you see, you still have to know the type of the callback, but at least you can use gstreamermm C++ classes.
There is couple of things to do in this code, like getting last parameter from the list in more efficient way than through the tuple, etc.
I don't feel it is stable enough to integrate it with gstreamermm, but probably in the future I'll do that. Also, we could even internally use it in the glibmm to reduce amount of generated code.

Links
[1] https://bugzilla.gnome.org/show_bug.cgi?id=755395
[2] https://gist.github.com/loganek/7833089caff73ff2e8b1f076c8f7910e

Monday, June 29, 2015

GStreamer Debugger - introduction

Hi everyone,
This year I've been accepted to Google Summer of Code :) Last year I worked on Banshee project [1], and this year I joined to GStreamer [2] team.
This summer I work on tool for GStreamer-based applications - GStreamer Debugger.

Goals
At the end of this summer, I'm going to provide you an application, which allows you to connect to your remote pipeline (obviously, lo interface can be used as well :)), watch pipeline graph (and its changes), spy selected queries, events, log messages, messages from bus and log messages, and even buffers. Application won't allow user modify pipeline and pipeline's state, but who knows - if it is useful feature, I implement it in the future.
GStreamer doesn't provide possibility to connect to pipeline, so I have to do it on my own.

Progress
June is a month, when I've exams on my university (fortunately, I've already passed all of them!), so I didn't spend as much time as I wanted on this project. Anyway, I accomplished a few milestones.
There's a list, what already has been done:
  • gst-trace [3] plugin, containing tcp server. For now, it sends GstEvents, GstMessages, and log messages to clients (todo: send GstBuffers, and GstQueries)
  • client application, which displays events and log messages (todo: display GstBuffers, GstQueries, GstMessages). I have a lot of ideas, how to improve client application, but I'm not sure whether I meet GSOC deadline, so I suppose, most of them will be implement after Google's program. 
  • protocol - I used Google Protobuf library [4]. In general, I've defined most of protocol's structures, I just make minor improvements, when I need it.
Below you can find a few screenshoots of client application. Full code can be found on my github account ([5], [6]).




Links
[1] http://www.cookandcommit.eu/2014/04/google-summer-of-code-2014-with-gnome.html
[2] http://gstreamer.freedesktop.org/
[3] https://bugzilla.gnome.org/show_bug.cgi?id=733187
[4] https://developers.google.com/protocol-buffers/
[5] https://github.com/loganek/gstreamer
[6] https://github.com/loganek/gst-debugger