Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RMagick, migrate to ImageMagick 7 #RubyKaigi #RubyKaigi2019

Watson
April 18, 2019

RMagick, migrate to ImageMagick 7 #RubyKaigi #RubyKaigi2019

Watson

April 18, 2019
Tweet

More Decks by Watson

Other Decks in Programming

Transcript

  1. RMagick,
    migrate to ImageMagick 7
    Shizuo Fujita

    View full-size slide

  2. self.inspect
    • @watson1978

    • Ubiregi, Inc

    • Ruby committer

    • RMagick maintainer

    View full-size slide

  3. What's RMagick

    View full-size slide

  4. "RMagick is an interface between the
    Ruby programming language and the
    ImageMagick image processing
    library."

    View full-size slide

  5. Active members
    ˏmockdeep
    ˏwatson1978 ˏdlemstra

    View full-size slide

  6. My Motivation

    View full-size slide

  7. I want to use Ruby ...
    •Web programming

    View full-size slide

  8. I want to use Ruby ...
    •Web programming

    •iOS programming

    View full-size slide

  9. I want to use Ruby ...
    •Web programming

    •iOS programming

    •Image processing

    View full-size slide

  10. I want to use Ruby ...
    •Web programming

    •iOS programming

    •Image processing

    •Machine learning, etc

    View full-size slide

  11. I want to use Ruby ...
    •Web programming

    •iOS programming

    •Image processing

    •Machine learning, etc
    To meet crazy
    great people
    at Ruby World

    View full-size slide

  12. Current Status of RMagick

    View full-size slide

  13. Fixed major problems
    since v2.16.0
    •Set up

    •Memory leaks

    •SEGVs

    View full-size slide

  14. Set up problems

    View full-size slide

  15. Set up problems
    •macOS (Homebrew)

    •Windows

    View full-size slide

  16. macOS (Homebrew)

    View full-size slide

  17. macOS (Homebrew)

    View full-size slide

  18. Windows
    •Ruby has some environments

    • MingW

    • MSWin

    • Cygwin

    • Windows Subsystem for Linux

    View full-size slide

  19. Windows
    •Ruby has some environments

    • MingW

    • MSWin

    • Cygwin

    • Windows Subsystem for Linux

    View full-size slide

  20. Set up : MingW

    View full-size slide

  21. Solution
    https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers

    View full-size slide

  22. Set up : MSWin

    View full-size slide

  23. MSWin supported...?
    Looks like MSWin
    supporting code...

    View full-size slide

  24. Not supported...
    Why !?
    For MSWin

    View full-size slide

  25. MSWin works well

    View full-size slide

  26. Fixed many memory leaks
    55 Closed

    View full-size slide

  27. Case 1
    VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }

    View full-size slide

  28. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory

    View full-size slide

  29. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory
    Destroy memory

    View full-size slide

  30. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory
    Destroy memory
    Raise exception with
    unexpected value

    View full-size slide

  31. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory
    Destroy memory
    Raise exception with
    unexpected value
    Unreachable by
    exception

    View full-size slide

  32. Case 1: Exception causes Memory Leak
    image =
    Magick::Image.read('sample.png').first
    begin
    image.shear('x', 'y')
    rescue
    end

    View full-size slide

  33. Case 2
    static VALUE
    has_attribute(VALUE self, MagickBooleanType....[snip])
    {
    Image *image;
    ExceptionInfo *exception;
    MagickBooleanType r;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    r = (attr_test)(image, exception);
    CHECK_EXCEPTION()
    return r ? Qtrue : Qfalse;
    }

    View full-size slide

  34. static VALUE
    has_attribute(VALUE self, MagickBooleanType....[snip])
    {
    Image *image;
    ExceptionInfo *exception;
    MagickBooleanType r;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    r = (attr_test)(image, exception);
    CHECK_EXCEPTION()
    return r ? Qtrue : Qfalse;
    }
    Case 2
    Allocate memory

    View full-size slide

  35. static VALUE
    has_attribute(VALUE self, MagickBooleanType....[snip])
    {
    Image *image;
    ExceptionInfo *exception;
    MagickBooleanType r;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    r = (attr_test)(image, exception);
    CHECK_EXCEPTION()
    return r ? Qtrue : Qfalse;
    }
    Case 2
    Allocate memory
    Missing destroy

    View full-size slide

  36. Case 2: Missing destroy lead to
    Memory Leak
    image =
    Magick::Image.read('sample.png').first
    image.gray?

    View full-size slide

  37. Case 3
    Unnecessary clone
    API returns NEW image
    in allocated memory

    View full-size slide

  38. Case 3: Memory Leak
    by incorrect API usage
    image =
    Magick::Image.read('sample.png').first
    image.sharpen_channel

    View full-size slide

  39. Case 4 Found
    memory leak in
    ImageMagick

    View full-size slide

  40. Case 4: Huge software has
    Memory Leak

    View full-size slide

  41. How to detect memory leak

    View full-size slide

  42. Profiler tool

    View full-size slide

  43. Profiler tool
    Memory leak
    detected

    View full-size slide

  44. Profiler tool

    View full-size slide

  45. Profiler tool...
    •Detects at executed code only

    •unit-test

    •RSpec

    •Needs better coverage...

    View full-size slide

  46. Code reading...
    •To find same pattern that profiler detects

    View full-size slide

  47. Closed 5 SEGVs
    4 Fixed
    1 Workaround

    View full-size slide

  48. Workaround Case
    Help wanted

    View full-size slide

  49. Workaround Case
    •Call back image processing progress to
    Ruby
    User's
    Ruby code
    RMagick ImageMagick
    Callback

    View full-size slide

  50. ruby_stack_length() shows
    51119378542 = 47 GB
    • Invalid stack length
    • GC running
    →SEGV

    View full-size slide

  51. RMagick is better than ever.

    View full-size slide

  52. ImageMagick 7 support

    View full-size slide

  53. Preparing draft code... now

    View full-size slide

  54. Plan 1: Split
    RMagick ver 4.x
    RMagick ver 3.x
    ver 3.0 ver 3.1
    ver 2.16.1
    ImageMagick 7
    ImageMagick 6

    View full-size slide

  55. Plan 2 : Mix both
    ImageMagick 7
    ImageMagick 7
    ImageMagick 6
    Using many #if defined
    ...

    View full-size slide

  56. Plan 3: Bundle both
    ImageMagick 7
    ImageMagick 6

    View full-size slide

  57. We welcome good ideas for
    the future

    View full-size slide

  58. Announcement
    •RubyData Workshop (Tomorrow, 14:20 ~)

    •RubyKaigi 2019 Code Party (Tomorrow, 19:00 ~)

    View full-size slide