https://bitbucket.org/hudson/magic-lantern
Raw File
Tip revision: 3c8ed7d6cda1a2c21294add1866d7d3f47dc0941 authored by alex@thinkpad on 24 September 2018, 10:00:01 UTC
io_trace: fix crash with CONFIG_QEMU
Tip revision: 3c8ed7d
tasks.c
// This module keeps track of ML tasks.
// It also displays info about DryOS tasks, and requests ML tasks to return at shutdown.
//
// Credits:
// * Indy for task information: http://groups.google.com/group/ml-devel/browse_thread/thread/26cb46acd262b953
// * AJ for the idea of shutting down ML tasks manually, rather than letting DryOS do this job

#include "dryos.h"
#include "property.h"
#include "bmp.h"
#include "tskmon.h"
#include "menu.h"
#include "crop-mode-hack.h"
#include "dm-spy.h"

/* for CBRs */
#include "config.h"
#include "lens.h"

extern int is_taskid_valid(int, int, void*);

int ml_shutdown_requested = 0;

char* get_current_task_name()
{
    /* DryOS: right after current_task we have a flag
     * set to 1 when handling an interrupt */
    uint32_t interrupt_active = MEM((uintptr_t)&current_task + 4);
    
    if (!interrupt_active)
    {
        return current_task->name;
    }
    else
    {
        static char isr[] = "**INT-00h**";
        int i = current_interrupt >> 2;
        int i0 = (i & 0xF);
        int i1 = (i >> 4) & 0xF;
        int i2 = (i >> 8) & 0xF;
        isr[5] = i2 ? '0' + i2 : '-';
        isr[6] = i1 < 10 ? '0' + i1 : 'A' + i1 - 10;
        isr[7] = i0 < 10 ? '0' + i0 : 'A' + i0 - 10;
        return isr;
    }
}

char* get_task_name_from_id(int id)
{
#if defined(CONFIG_VXWORKS)
return "?";
#endif
    if(id < 0)
    {
        return "?";
    }
    
    char* name = "?";
    int c = id & 0xFF;

    struct task_attr_str task_attr;
    int r = is_taskid_valid(1, c, &task_attr); // ok
    if (r==0) {
      if (task_attr.name!=0) name=task_attr.name;
      else name="?";
    }
    return name;
}

#ifndef CONFIG_VXWORKS
#ifdef CONFIG_TSKMON
#if defined(FEATURE_SHOW_CPU_USAGE) || defined(FEATURE_SHOW_TASKS)
static taskload_t tskmon_task_loads[TSKMON_MAX_TASKS];
#endif
#ifdef FEATURE_SHOW_CPU_USAGE
int show_cpu_usage_flag = 0;
#endif
#ifdef FEATURE_SHOW_TASKS
static int task_load_update_request = 0;
#endif
#endif

void task_update_loads() // called every second from clock_task
{
#ifdef CONFIG_TSKMON
    #ifdef FEATURE_SHOW_CPU_USAGE
    if (show_cpu_usage_flag)
    {
        static int k = 0; k++;
        if (k & 1) return; // update every 2 seconds
        
        int cu = tskmon_update_loads(tskmon_task_loads);
        
        #if defined(CONFIG_7D)
        int x = 500;
        #else
        int x = 50;
        #endif
        int c = cu > 800 ? COLOR_RED : cu > 300 ? COLOR_YELLOW : cu > 100 ? COLOR_CYAN : COLOR_WHITE;
        bmp_printf(FONT(FONT_MED, c, COLOR_BLACK), x, 50, "CPU: %3d.%d%% ", cu/10, cu%10);
        
        if (show_cpu_usage_flag >= 2)
        {
            int y = 50 + font_med.height;
            for (int i = 0; i < TSKMON_MAX_TASKS; i++)
            {
                int cpu_percent = show_cpu_usage_flag == 2 ? tskmon_task_loads[i].absolute : tskmon_task_loads[i].relative;
                if (cpu_percent)
                {
                    char* name = "";
                    
                    if(i < TSKMON_MAX_TASKS-1)
                    {
                        name = get_task_name_from_id(i);
                        if (streq(name, "PowerMgr"))
                            continue;
                    }
                    else
                    {
                        name = "Interrupts";
                    }
                    char short_name[] = "           \0";
                    memcpy(short_name, name, MIN(sizeof(short_name)-2, strlen(name)));
                    int c = cpu_percent > 800 ? COLOR_RED : cpu_percent > 300 ? COLOR_YELLOW : cpu_percent > 100 ? COLOR_CYAN : (i == TSKMON_MAX_TASKS-1) ? COLOR_ORANGE : COLOR_WHITE;
                    bmp_printf(FONT(FONT_SMALL, c, COLOR_BLACK), x, y, 
                    "%s: %2d.%1d%%", 
                    short_name, cpu_percent/10, cpu_percent%10);
                    y += font_small.height;
                    if (y > 400) break;
                }
            }
            
            static int prev_y = 0;
            if (y < prev_y) bmp_fill(0, x, y, font_small.width*18, prev_y - y);
            prev_y = y;
        }
    }
    #endif
    
    #ifdef FEATURE_SHOW_TASKS
    if (task_load_update_request) // for menu
    {
        tskmon_update_loads(tskmon_task_loads);
        task_load_update_request = 0;
    }
    #endif
#endif
}
#endif

#if 0 // for debugging only (tskmon checks all tasks in background, so it shouldn't be needed)

/* manually checks peak stack usage for current task (just call it from any task) */
/* returns free stack space */
int task_check_stack()
{
    struct task_attr_str task_attr;
    int id = current_task->taskId;

    /* works, gives the same result as DryOS routine, so... let's just use the DryOS one
     *
    #ifdef CONFIG_TSKMON
    tskmon_stack_check(id);
    msleep(50); // wait until the task is rescheduled, so tskmon can check it
    uint32_t stack_used = 0;
    uint32_t stack_free = 0;
    tskmon_stack_get_max(id, &stack_used, &stack_free);
    bmp_printf(FONT_MED, 0, 0, "free: %d used: %d", stack_free, stack_used);
    return stack_free;
    #elif !defined(CONFIG_VXWORKS)
    */
    
    int r = is_taskid_valid(1, id, &task_attr);
    if (r == 0)
    {
        int free = task_attr.size - task_attr.used;
        bmp_printf(FONT(FONT_MED, free ? COLOR_WHITE : COLOR_RED, COLOR_BLACK), 0, 0, "%s: stack free: %d used: %d   ", get_task_name_from_id(id), free, task_attr.used);
        return free;
    }
    return -1;
    //#endif
}
#endif

#ifdef FEATURE_SHOW_TASKS
static int tasks_show_flags = 0;

MENU_SELECT_FUNC(tasks_toggle_flags)
{
    #ifdef CONFIG_TSKMON
    menu_numeric_toggle(&tasks_show_flags, delta, 0, 3);
    #else
    menu_numeric_toggle(&tasks_show_flags, delta, 0, 1);
    #endif
}

MENU_UPDATE_FUNC(tasks_print)
{
    if (!info->can_custom_draw) return;

    info->custom_drawing = CUSTOM_DRAW_THIS_MENU;
    
#if defined(CONFIG_VXWORKS)

    if (entry->selected) 
    {
        bmp_fill(40, 0, 0, 720, 480);
    }

    static int task_info[100];
    static int tasks[100];
    int N = get_active_task_list(tasks, 100);
    int x = 5, y = 5;
    bmp_printf(FONT_MED, x, y, (tasks_show_flags & 1) ? "Canon tasks" : "ML tasks");
    y += font_med.height;
    
    for (int i = 0; i < N; i++)
    {
        get_task_info(tasks[i], task_info);
        
        char* name = (char*) task_info[1]+1;
        char short_name[] = "             \0";
        memcpy(short_name, name, MIN(sizeof(short_name)-2, strlen(name)));

        // Canon tasks are named in uppercase (exception: idle); ML tasks are named in lowercase.
        int is_canon_task = (name[0]  < 'a' || name[0] > 'z' || name[1]  < 'a' || name[1] > 'z');
        if((tasks_show_flags & 1) != is_canon_task)
        {
            continue;
        }

        int stack_size = task_info[10]; // from task_create calls
        int stack_unused_maybe = task_info[13];
        int mem_percent = 100 - stack_unused_maybe * 100 / stack_size; // seems OK, when higher than 100, camera no longer boots
        bmp_printf(SHADOW_FONT(FONT(FONT_MED, mem_percent < 50 ? COLOR_WHITE : mem_percent < 90 ? COLOR_YELLOW : COLOR_RED, 40)), 
            x, y, "%s: p=%d m=%d%%", 
            short_name, task_info[2], mem_percent);
        y += font_med.height-1;
        if (y > 460)
        {
            x += 360;
            y = 10;
        }
    }

#else // DryOS - https://groups.google.com/forum/#!msg/ml-devel/JstGrNJiuVM/2L1QZpZ7F4YJ

    #ifdef CONFIG_TSKMON
    task_load_update_request = 1; // will update at next second clock tick
    #endif

    if (entry->selected) 
    {
        bmp_fill(38, 0, 0, 720, 480);
    }

    int task_id;
    unsigned int r;
    struct task_attr_str task_attr;
    char *name;
    extern unsigned int task_max;

    // wait_id: 0=sleep, 1=sem, 2=flg/event, 3=sendmq, 4=recvmq, 5=mutex
    // state: 0=ready, 1=wait, 2=susp, other=wait+s

    int x = 5;
    int y = 5;

    bmp_printf(FONT_MED, x, y, 
        tasks_show_flags == 0 ? "ML tasks" :
        tasks_show_flags == 1 ? "Canon tasks" :
        tasks_show_flags == 2 ? "ML tasks (CPU)" :
                                "Canon tasks (CPU)"
        );
    y += font_med.height;

    task_id = 1;
    
    int total_tasks = 0;
    for (task_id=1; task_id<=(int)task_max; task_id++)
    {
        r = is_taskid_valid(1, task_id, &task_attr); // ok
        if (r==0)
        {
            total_tasks++;

            if (task_attr.name!=0)
            {
                name=task_attr.name;
            }
            else
            {
                name="?";
            }

            // Canon tasks are named in uppercase (exception: idle); ML tasks are named in lowercase.
            int is_canon_task = (name[0]  < 'a' || name[0] > 'z' || streq(name, "idle") ||  streq(name, "systemtask"));
            if((tasks_show_flags & 1) != is_canon_task)
            {
                continue;
            }

            char short_name[] = "                    \0";
            
            memcpy(short_name, name, MIN(sizeof(short_name)-2, strlen(name)));

            #ifdef CONFIG_TSKMON
            /* print stack/cpu usage details */
            if(tasks_show_flags & 2)
            {
                int cpu_percent_abs = tskmon_task_loads[task_id].absolute / 10;
                int cpu_percent_abs_dec = tskmon_task_loads[task_id].absolute % 10;
                int cpu_percent_rel = tskmon_task_loads[task_id].relative / 10;
                int cpu_percent_rel_dec = tskmon_task_loads[task_id].relative % 10;
                
                bmp_printf(SHADOW_FONT(FONT(FONT_SMALL, cpu_percent_rel < 50 ? COLOR_WHITE : cpu_percent_rel < 90 ? COLOR_YELLOW : COLOR_RED, 38)), x, y, 
                "%3d %s: a=%2d.%1d%% r=%2d.%1d%%\n", 
                task_id, short_name, cpu_percent_abs, cpu_percent_abs_dec, 0, cpu_percent_rel, cpu_percent_rel_dec, 0);
            }
            else
            {
                uint32_t stack_used = 0;
                uint32_t stack_free = 0;
                
                tskmon_stack_check(task_id);
                tskmon_stack_get_max(task_id, &stack_used, &stack_free);
                
                int mem_percent = stack_used * 100 / task_attr.size;
                
                uint32_t color = mem_percent < 50 ? COLOR_WHITE : mem_percent < 90 ? COLOR_YELLOW : COLOR_RED;
                
                if(stack_free == 0)
                {
                    color = 50;
                }
                
                bmp_printf(SHADOW_FONT(FONT(FONT_SMALL, color, 38)), x, y, 
                "%3d %s: p=%2x w=%2x m=%2d%% %d\n", 
                task_id, short_name, task_attr.pri, task_attr.wait_id, mem_percent, 0, task_attr.state);
            }
            #else
                int mem_percent = task_attr.used * 100 / task_attr.size;
                bmp_printf(SHADOW_FONT(FONT(FONT_SMALL, task_id >= 99 ? COLOR_RED : COLOR_WHITE, 38)), x, y, 
                "%3d %s: p=%2x w=%2x m=%2d%% %d\n", 
                task_id, short_name, task_attr.pri, task_attr.wait_id, mem_percent, 0, task_attr.state);
            #endif

            #if defined(CONFIG_60D) || defined(CONFIG_7D) || defined(CONFIG_DIGIC_V)
            y += font_small.height - ((tasks_show_flags & 1) ? 1 : 0); // too many tasks - they don't fit on the screen :)
            #else
            y += font_small.height;
            #endif
            if (y > 460)
            {
                x += 360;
                y = 10 + font_med.height;
            }
        }
    }
    bmp_printf(
        FONT(FONT_MED, COLOR_GRAY(30), COLOR_BLACK), 
        720 - font_med.width * 9, 5, 
        "[%d/%d]", total_tasks, task_max
    );
#endif
}
#endif

#ifdef FEATURE_GPS_TWEAKS
#include "gps.h"
#endif

static void ml_shutdown()
{
    check_pre_shutdown_flag();
#ifdef FEATURE_CROP_MODE_HACK
    movie_crop_hack_disable();
#endif
    ml_shutdown_requested = 1;
    
    info_led_on();
    _card_led_on();
    restore_af_button_assignment_at_shutdown();
#ifdef FEATURE_GPS_TWEAKS
    gps_tweaks_shutdown_hook();
#endif    
    config_save_at_shutdown();
#if defined(CONFIG_MODULES)
    /* to refactor with CBR */
    extern int module_shutdown();
    module_shutdown();
#endif

#if defined(CONFIG_DEBUG_INTERCEPT)
    if (debug_intercept_running())
    {
        /* any logging in progress? save it now */
        debug_intercept();
    }
#endif

    info_led_on();
    _card_led_on();
}

PROP_HANDLER(PROP_TERMINATE_SHUT_REQ)
{
    //bmp_printf(FONT_MED, 0, 0, "SHUT REQ %d ", buf[0]);
    if (buf[0] == 0)  ml_shutdown();
}

#if 0
static int task_holding_bmp_lock = 0;
static int line_holding_bmp_lock = 0;
static char func_holding_bmp_lock[50] = "";

int CheckBmpAcquireRecursiveLock(void* lock, int line, const char* func)
{
    char* task_name = get_current_task_name();
    
    // just a warning, sometimes we can't get without it (e.g. at redraw), but it's best to avoid
    /*
    if (streq(task_name, "GuiMainTask"))
    {
        int x = 100;
        bmp_puts(FONT_MED, &x, &x, "BMP_LOCK GMT");
    }*/
    
    // this is really bad - don't ever try to block property handling task!
    if (streq(task_name, "PropMgr"))
    {
        extern int current_prop_handler;
        char msg[50];
        snprintf(msg, sizeof(msg), "BMP_LOCK PROP %x!!!", current_prop_handler);
        int x = 100;
        bmp_puts(FONT_MED, (unsigned int *)&x, (unsigned int *)&x, msg);
        beep();
        info_led_blink(20,50,50);
        ASSERT(0);
    }

    int wait = 2000;
    int r;
    while ((r = (int)AcquireRecursiveLock(lock, wait)))
    {
        char msg[100];
        snprintf(msg, sizeof(msg), "%s:%s:%d:\nRLock held by %s:%s:%d  ", get_current_task_name(), func, line, get_task_name_from_id(task_holding_bmp_lock), func_holding_bmp_lock, line_holding_bmp_lock);//, get_task_name_from_id(task_holding_bmp_lock));
        int x = 100;
        bmp_puts(FONT_MED, (unsigned int *)&x, (unsigned int *)&x, msg);
        ml_assert_handler(msg, __FILE__, __LINE__, __func__);
        wait = 0;
    }
    task_holding_bmp_lock = current_task->taskId;
    line_holding_bmp_lock = line;
    snprintf(func_holding_bmp_lock, sizeof(func_holding_bmp_lock), func);
    return r;
}

int CheckBmpReleaseRecursiveLock(void* lock)
{
    int r = (int)ReleaseRecursiveLock(lock);
    //~ task_holding_bmp_lock = -1;
    //~ char msg[50] = "                  ";
    //~ int x = 100;
    //~ bmp_puts(FONT_LARGE, &x, &x, msg);
    return r;
}
#endif
back to top