diff options
75 files changed, 2916 insertions, 915 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 4ed63b6cfb15..025b7cf3768d 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -198,6 +198,31 @@ Description: time (in microseconds) this cpu should spend in this idle state to make the transition worth the effort. +What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/ +Date: March 2018 +KernelVersion: v4.17 +Contact: Linux power management list <linux-pm@vger.kernel.org> +Description: + Idle state usage statistics related to suspend-to-idle. + + This attribute group is only present for states that can be + used in suspend-to-idle with suspended timekeeping. + +What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/time +Date: March 2018 +KernelVersion: v4.17 +Contact: Linux power management list <linux-pm@vger.kernel.org> +Description: + Total time spent by the CPU in suspend-to-idle (with scheduler + tick suspended) after requesting this state. + +What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/usage +Date: March 2018 +KernelVersion: v4.17 +Contact: Linux power management list <linux-pm@vger.kernel.org> +Description: + Total number of times this state has been requested by the CPU + while entering suspend-to-idle. What: /sys/devices/system/cpu/cpu#/cpufreq/* Date: pre-git history diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index 1e0d1dac706b..2f813d644c69 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -287,3 +287,17 @@ Description: Writing a "1" to this file enables the debug messages and writing a "0" (default) to it disables them. Reads from this file return the current value. + +What: /sys/power/resume_offset +Date: April 2018 +Contact: Mario Limonciello <mario.limonciello@dell.com> +Description: + This file is used for telling the kernel an offset into a disk + to use when hibernating the system such as with a swap file. + + Reads from this file will display the current offset + the kernel will be using on the next hibernation + attempt. + + Using this sysfs file will override any values that were + set using the kernel command line for disk offset.
\ No newline at end of file diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt index dd3929e85dec..332aed8f4597 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt @@ -18,8 +18,6 @@ Optional properties: in unit of nanoseconds. - voltage-tolerance: Specify the CPU voltage tolerance in percentage. - #cooling-cells: -- cooling-min-level: -- cooling-max-level: Please refer to Documentation/devicetree/bindings/thermal/thermal.txt. Examples: @@ -40,8 +38,6 @@ cpus { >; clock-latency = <61036>; /* two CLK32 periods */ #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <2>; }; cpu@1 { diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt index f6403089edcf..d36f07e0a2bb 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt @@ -21,8 +21,6 @@ Optional properties: flow is handled by hardware, hence no software "voltage tracking" is needed. - #cooling-cells: -- cooling-min-level: -- cooling-max-level: Please refer to Documentation/devicetree/bindings/thermal/thermal.txt for detail. @@ -67,8 +65,6 @@ Example 1 (MT7623 SoC): clock-names = "cpu", "intermediate"; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <7>; }; cpu@1 { device_type = "cpu"; diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 9f2f942a01cf..cc87adf44c0a 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -24,8 +24,16 @@ Some warnings, first. * see the FAQ below for details. (This is not true for more traditional * power states like "standby", which normally don't turn USB off.) +Swap partition: You need to append resume=/dev/your_swap_partition to kernel command -line. Then you suspend by +line or specify it using /sys/power/resume. + +Swap file: +If using a swapfile you can also specify a resume offset using +resume_offset=<number> on the kernel command line or specify it +in /sys/power/resume_offset. + +After preparing then you suspend by echo shutdown > /sys/power/disk; echo disk > /sys/power/state diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi index b750da5362f7..8e7c65464c9d 100644 --- a/arch/arm/boot/dts/mt7623.dtsi +++ b/arch/arm/boot/dts/mt7623.dtsi @@ -87,8 +87,6 @@ clock-names = "cpu", "intermediate"; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <7>; clock-frequency = <1300000000>; }; diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index c4d0a1c912f0..3d96e4da2d98 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -543,6 +543,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, unsigned long long ret; int d_min, d_max; bool wakeup = false; + bool has_sxd = false; acpi_status status; /* @@ -581,6 +582,10 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, else return -ENODATA; } + + if (status == AE_OK) + has_sxd = true; + d_min = ret; wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid && adev->wakeup.sleep_state >= target_state; @@ -599,7 +604,11 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, method[3] = 'W'; status = acpi_evaluate_integer(handle, method, NULL, &ret); if (status == AE_NOT_FOUND) { - if (target_state > ACPI_STATE_S0) + /* No _SxW. In this case, the ACPI spec says that we + * must not go into any power state deeper than the + * value returned from _SxD. + */ + if (has_sxd && target_state > ACPI_STATE_S0) d_max = d_min; } else if (ACPI_SUCCESS(status) && ret <= ACPI_STATE_D3_COLD) { /* Fall back to D3cold if ret is not a valid state. */ diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index b35923e3a926..99a1a650326d 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -849,23 +849,25 @@ static void lpi_check_constraints(void) int i; for (i = 0; i < lpi_constraints_table_size; ++i) { + acpi_handle handle = lpi_constraints_table[i].handle; struct acpi_device *adev; - if (acpi_bus_get_device(lpi_constraints_table[i].handle, &adev)) + if (!handle || acpi_bus_get_device(handle, &adev)) continue; - acpi_handle_debug(adev->handle, + acpi_handle_debug(handle, "LPI: required min power state:%s current power state:%s\n", acpi_power_state_string(lpi_constraints_table[i].min_dstate), acpi_power_state_string(adev->power.state)); if (!adev->flags.power_manageable) { - acpi_handle_info(adev->handle, "LPI: Device not power manageble\n"); + acpi_handle_info(handle, "LPI: Device not power manageable\n"); + lpi_constraints_table[i].handle = NULL; continue; } if (adev->power.state < lpi_constraints_table[i].min_dstate) - acpi_handle_info(adev->handle, + acpi_handle_info(handle, "LPI: Constraint not met; min power state:%s current power state:%s\n", acpi_power_state_string(lpi_constraints_table[i].min_dstate), acpi_power_state_string(adev->power.state)); @@ -951,15 +953,8 @@ static int acpi_s2idle_prepare(void) if (lps0_device_handle) { acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY); - } else { - /* - * The configuration of GPEs is changed here to avoid spurious - * wakeups, but that should not be necessary if this is a - * "low-power S0" platform and the low-power S0 _DSM is present. - */ - acpi_enable_all_wakeup_gpes(); - acpi_os_wait_events_complete(); } + if (acpi_sci_irq_valid()) enable_irq_wake(acpi_sci_irq); @@ -992,8 +987,9 @@ static void acpi_s2idle_sync(void) * The EC driver uses the system workqueue and an additional special * one, so those need to be flushed too. */ + acpi_os_wait_events_complete(); /* synchronize SCI IRQ handling */ acpi_ec_flush_work(); - acpi_os_wait_events_complete(); + acpi_os_wait_events_complete(); /* synchronize Notify handling */ s2idle_wakeup = false; } @@ -1005,8 +1001,6 @@ static void acpi_s2idle_restore(void) if (lps0_device_handle) { acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT); acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON); - } else { - acpi_enable_all_runtime_gpes(); } } diff --git a/drivers/base/core.c b/drivers/base/core.c index 5847364f25d9..b610816eb887 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -196,8 +196,10 @@ struct device_link *device_link_add(struct device *consumer, } list_for_each_entry(link, &supplier->links.consumers, s_node) - if (link->consumer == consumer) + if (link->consumer == consumer) { + kref_get(&link->kref); goto out; + } link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) @@ -222,6 +224,7 @@ struct device_link *device_link_add(struct device *consumer, link->consumer = consumer; INIT_LIST_HEAD(&link->c_node); link->flags = flags; + kref_init(&link->kref); /* Determine the initial link state. */ if (flags & DL_FLAG_STATELESS) { @@ -292,8 +295,10 @@ static void __device_link_free_srcu(struct rcu_head *rhead) device_link_free(container_of(rhead, struct device_link, rcu_head)); } -static void __device_link_del(struct device_link *link) +static void __device_link_del(struct kref *kref) { + struct device_link *link = container_of(kref, struct device_link, kref); + dev_info(link->consumer, "Dropping the link to %s\n", dev_name(link->supplier)); @@ -305,8 +310,10 @@ static void __device_link_del(struct device_link *link) call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu); } #else /* !CONFIG_SRCU */ -static void __device_link_del(struct device_link *link) +static void __device_link_del(struct kref *kref) { + struct device_link *link = container_of(kref, struct device_link, kref); + dev_info(link->consumer, "Dropping the link to %s\n", dev_name(link->supplier)); @@ -324,13 +331,15 @@ static void __device_link_del(struct device_link *link) * @link: Device link to delete. * * The caller must ensure proper synchronization of this function with runtime - * PM. + * PM. If the link was added multiple times, it needs to be deleted as often. + * Care is required for hotplugged devices: Their links are purged on removal + * and calling device_link_del() is then no longer allowed. */ void device_link_del(struct device_link *link) { device_links_write_lock(); device_pm_lock(); - __device_link_del(link); + kref_put(&link->kref, __device_link_del); device_pm_unlock(); device_links_write_unlock(); } @@ -444,7 +453,7 @@ static void __device_links_no_driver(struct device *dev) continue; if (link->flags & DL_FLAG_AUTOREMOVE) - __device_link_del(link); + kref_put(&link->kref, __device_link_del); else if (link->status != DL_STATE_SUPPLIER_UNBIND) WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } @@ -597,13 +606,13 @@ static void device_links_purge(struct device *dev) list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) { WARN_ON(link->status == DL_STATE_ACTIVE); - __device_link_del(link); + __device_link_del(&link->kref); } list_for_each_entry_safe_reverse(link, ln, &dev->links.consumers, s_node) { WARN_ON(link->status != DL_STATE_DORMANT && link->status != DL_STATE_NONE); - __device_link_del(link); + __device_link_del(&link->kref); } device_links_write_unlock(); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 21244c53e377..86e67e70b509 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -31,6 +31,7 @@ struct wake_irq { struct device *dev; unsigned int status; int irq; + const char *name; }; extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 6637fc319269..b8fa5c0f2d13 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -112,6 +112,7 @@ void dev_pm_clear_wake_irq(struct device *dev) free_irq(wirq->irq, wirq); wirq->status &= ~WAKE_IRQ_DEDICATED_MASK; } + kfree(wirq->name); kfree(wirq); } EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); @@ -184,6 +185,12 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) if (!wirq) return -ENOMEM; + wirq->name = kasprintf(GFP_KERNEL, "%s:wakeup", dev_name(dev)); + if (!wirq->name) { + err = -ENOMEM; + goto err_free; + } + wirq->dev = dev; wirq->irq = irq; irq_set_status_flags(irq, IRQ_NOAUTOEN); @@ -196,9 +203,9 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) * so we use a threaded irq. */ err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq, - IRQF_ONESHOT, dev_name(dev), wirq); + IRQF_ONESHOT, wirq->name, wirq); if (err) - goto err_free; + goto err_free_name; err = dev_pm_attach_wake_irq(dev, irq, wirq); if (err) @@ -210,6 +217,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) err_free_irq: free_irq(irq, wirq); +err_free_name: + kfree(wirq->name); err_free: kfree(wirq); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index fb586e09682d..833b5f41f596 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -45,6 +45,7 @@ config ARM_DT_BL_CPUFREQ config ARM_SCPI_CPUFREQ tristate "SCPI based CPUfreq driver" depends on ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI + depends on !CPU_THERMAL || THERMAL help This adds the CPUfreq driver support for ARM platforms using SCPI protocol for CPU power management. diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index d0c34df0529c..9449657d72f0 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -794,15 +794,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) valid_states++; } freq_table[valid_states].frequency = CPUFREQ_TABLE_END; + policy->freq_table = freq_table; perf->state = 0; - result = cpufreq_table_validate_and_show(policy, freq_table); - if (result) - goto err_freqfree; - - if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq) - pr_warn(FW_WARN "P-state 0 is not max freq\n"); - switch (perf->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: /* @@ -842,8 +836,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) return result; -err_freqfree: - kfree(freq_table); err_unreg: acpi_processor_unregister_performance(cpu); err_free_mask: @@ -871,6 +863,15 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) return 0; } +static void acpi_cpufreq_cpu_ready(struct cpufreq_policy *policy) +{ + struct acpi_processor_performance *perf = per_cpu_ptr(acpi_perf_data, + policy->cpu); + + if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq) + pr_warn(FW_WARN "P-state 0 is not max freq\n"); +} + static int acpi_cpufreq_resume(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = policy->driver_data; @@ -898,6 +899,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .bios_limit = acpi_processor_get_bios_limit, .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, + .ready = acpi_cpufreq_cpu_ready, .resume = acpi_cpufreq_resume, .name = "acpi-cpufreq", .attr = acpi_cpufreq_attr, diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index c56b57dcfda5..1d7ef5fc1977 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -483,14 +483,7 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) if (ret) return ret; - ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]); - if (ret) { - dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n", - policy->cpu, cur_cluster); - put_cluster_clk_and_freq_table(cpu_dev, policy->cpus); - return ret; - } - + policy->freq_table = freq_table[cur_cluster]; policy->cpuinfo.transition_latency = arm_bL_ops->get_transition_latency(cpu_dev); diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 7281a2c19c36..6cdac1aaf23c 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -902,11 +902,7 @@ static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy) return ret; } - ret = cpufreq_table_validate_and_show(policy, freq_table); - if (ret) { - dev_err(dev, "invalid frequency table: %d\n", ret); - return ret; - } + policy->freq_table = freq_table; /* All cores share the same clock and thus the same policy. */ cpumask_setall(policy->cpus); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index a1c3025f9df7..8300a9fcb80c 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -20,6 +20,7 @@ #include <linux/cpu.h> #include <linux/cpufreq.h> #include <linux/dmi.h> +#include <linux/time.h> #include <linux/vmalloc.h> #include <asm/unaligned.h> @@ -162,6 +163,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq = cppc_dmi_max_khz; policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num); + policy->transition_delay_us = cppc_get_transition_latency(cpu_num) / + NSEC_PER_USEC; policy->shared_type = cpu->shared_type; if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) @@ -230,8 +233,13 @@ static int __init cppc_cpufreq_init(void) return ret; out: - for_each_possible_cpu(i) - kfree(all_cpu_data[i]); + for_each_possible_cpu(i) { + cpu = all_cpu_data[i]; + if (!cpu) + break; + free_cpumask_var(cpu->shared_cpu_map); + kfree(cpu); + } kfree(all_cpu_data); return -ENODEV; diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index de3d104c25d7..190ea0dccb79 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -258,16 +258,10 @@ static int cpufreq_init(struct cpufreq_policy *policy) priv->cpu_dev = cpu_dev; policy->driver_data = priv; policy->clk = cpu_clk; + policy->freq_table = freq_table; policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000; - ret = cpufreq_table_validate_and_show(policy, freq_table); - if (ret) { - dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, - ret); - goto out_free_cpufreq_table; - } - /* Support turbo/boost mode */ if (policy_has_boost_freq(policy)) { /* This gets disabled by core on driver unregister */ diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index de33ebf008ad..075d18f6ba7a 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -178,14 +178,7 @@ int cpufreq_generic_init(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table, unsigned int transition_latency) { - int ret; - - ret = cpufreq_table_validate_and_show(policy, table); - if (ret) { - pr_err("%s: invalid frequency table: %d\n", __func__, ret); - return ret; - } - + policy->freq_table = table; policy->cpuinfo.transition_latency = transition_latency; /* @@ -1219,6 +1212,10 @@ static int cpufreq_online(unsigned int cpu) goto out_free_policy; } + ret = cpufreq_table_validate_and_sort(policy); + if (ret) + goto out_exit_policy; + down_write(&policy->rwsem); if (new_policy) { @@ -1249,7 +1246,7 @@ static int cpufreq_online(unsigned int cpu) policy->cur = cpufreq_driver->get(policy->cpu); if (!policy->cur) { pr_err("%s: ->get() failed\n", __func__); - goto out_exit_policy; + goto out_destroy_policy; } } @@ -1296,7 +1293,7 @@ static int cpufreq_online(unsigned int cpu) if (new_policy) { ret = cpufreq_add_dev_interface(policy); if (ret) - goto out_exit_policy; + goto out_destroy_policy; cpufreq_stats_create_table(policy); @@ -1311,7 +1308,7 @@ static int cpufreq_online(unsigned int cpu) __func__, cpu, ret); /* cpufreq_policy_free() will notify based on this */ new_policy = false; - goto out_exit_policy; + goto out_destroy_policy; } up_write(&policy->rwsem); @@ -1326,15 +1323,16 @@ static int cpufreq_online(unsigned int cpu) return 0; -out_exit_policy: +out_destroy_policy: + for_each_cpu(j, policy->real_cpus) + remove_cpu_dev_symlink(policy, get_cpu_device(j)); + up_write(&policy->rwsem); +out_exit_policy: if (cpufreq_driver->exit) cpufreq_driver->exit(policy); - for_each_cpu(j, policy->real_cpus) - remove_cpu_dev_symlink(policy, get_cpu_device(j)); - out_free_policy: cpufreq_policy_free(policy); return ret; diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index cdf097b29862..60bea302abbe 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -184,7 +184,6 @@ static int eps_cpu_init(struct cpufreq_policy *policy) struct cpuinfo_x86 *c = &cpu_data(0); struct cpufreq_frequency_table *f_table; int k, step, voltage; - int ret; int states; #if IS_ENABLED(CONFIG_ACPI_PROCESSOR) unsigned int limit; @@ -359,12 +358,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) } policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */ - - ret = cpufreq_table_validate_and_show(policy, ¢aur->freq_table[0]); - if (ret) { - kfree(centaur); - return ret; - } + policy->freq_table = ¢aur->freq_table[0]; return 0; } diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index 45e2ca62515e..03419f064752 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -165,7 +165,8 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy) if (pos->frequency > max_freq) pos->frequency = CPUFREQ_ENTRY_INVALID; - return cpufreq_table_validate_and_show(policy, elanfreq_table); + policy->freq_table = elanfreq_table; + return 0; } diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 6d007f824ca7..10e119ae66dd 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -362,10 +362,24 @@ int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, return ret; policy->freq_table = table; - return set_freq_table_sorted(policy); + return 0; } EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show); +int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy) +{ + int ret; + + if (!policy->freq_table) + return 0; + + ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table); + if (ret) + return ret; + + return set_freq_table_sorted(policy); +} + MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION("CPUfreq frequency table helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index a757c0a1e7b5..7974a2fdb760 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -270,10 +270,7 @@ acpi_cpufreq_cpu_init ( } } - result = cpufreq_table_validate_and_show(policy, freq_table); - if (result) { - goto err_freqfree; - } + policy->freq_table = freq_table; /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); @@ -296,8 +293,6 @@ acpi_cpufreq_cpu_init ( return (result); - err_freqfree: - kfree(freq_table); err_unreg: acpi_processor_unregister_performance(cpu); err_free: diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index ff67859948b3..83cf631fc9bc 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -52,6 +52,7 @@ static struct clk_bulk_data clks[] = { static struct device *cpu_dev; static bool free_opp; static struct cpufreq_frequency_table *freq_table; +static unsigned int max_freq; static unsigned int transition_latency; static u32 *imx6_soc_volt; @@ -196,7 +197,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy) policy->clk = clks[ARM].clk; ret = cpufreq_generic_init(policy, freq_table, transition_latency); - policy->suspend_freq = policy->max; + policy->suspend_freq = max_freq; return ret; } @@ -437,12 +438,12 @@ soc_opp_out: * freq_table initialised from OPP is therefore sorted in the * same order. */ + max_freq = freq_table[--num].frequency; opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[0].frequency * 1000, true); min_volt = dev_pm_opp_get_voltage(opp); dev_pm_opp_put(opp); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[--num].frequency * 1000, true); + opp = dev_pm_opp_find_freq_exact(cpu_dev, max_freq * 1000, true); max_volt = dev_pm_opp_get_voltage(opp); dev_pm_opp_put(opp); diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index f730b6528c18..61a4c5b08219 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -895,8 +895,9 @@ static int longhaul_cpu_init(struct cpufreq_policy *policy) longhaul_setup_voltagescaling(); policy->transition_delay_us = 200000; /* usec */ + policy->freq_table = longhaul_table; - return cpufreq_table_validate_and_show(policy, longhaul_table); + return 0; } static struct cpufreq_driver longhaul_driver = { diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 8c04dddd3c28..eb8920d39818 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -460,21 +460,12 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy) return ret; } - ret = cpufreq_table_validate_and_show(policy, freq_table); - if (ret) { - pr_err("%s: invalid frequency table: %d\n", __func__, ret); - goto out_free_cpufreq_table; - } - cpumask_copy(policy->cpus, &info->cpus); + policy->freq_table = freq_table; policy->driver_data = info; policy->clk = info->cpu_clk; return 0; - -out_free_cpufreq_table: - dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table); - return ret; } static int mtk_cpufreq_exit(struct cpufreq_policy *policy) @@ -578,7 +569,7 @@ static int __init mtk_cpufreq_driver_init(void) match = of_match_node(mtk_cpufreq_machines, np); of_node_put(np); if (!match) { - pr_warn("Machine is not compatible with mtk-cpufreq\n"); + pr_debug("Machine is not compatible with mtk-cpufreq\n"); return -ENODEV; } diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index a25741b1281b..911206243050 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -202,8 +202,9 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) /* the transition latency is set to be 1 higher than the maximum * transition latency of the ondemand governor */ policy->cpuinfo.transition_latency = 10000001; + policy->freq_table = &p4clockmod_table[0]; - return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]); + return 0; } diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index dedd2568e852..8a8ea68611a8 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -214,8 +214,9 @@ have_busfreq: /* cpuinfo and default policy values */ policy->cpuinfo.transition_latency = 500000; + policy->freq_table = clock_ratio; - return cpufreq_table_validate_and_show(policy, clock_ratio); + return 0; } diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 302e9ce793a0..d6cb052b0a75 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -639,8 +639,9 @@ static int powernow_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency); + policy->freq_table = powernow_table; - return cpufreq_table_validate_and_show(policy, powernow_table); + return 0; } static int powernow_cpu_exit(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index b01e31db5f83..fb77b39a4ce3 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -122,14 +122,12 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data) static void count_off_irt(struct powernow_k8_data *data) { udelay((1 << data->irt) * 10); - return; } /* the voltage stabilization time */ static void count_off_vst(struct powernow_k8_data *data) { udelay(data->vstable * VST_UNITS_20US); - return; } /* need to init the control msr to a safe value (for each cpu) */ @@ -591,10 +589,8 @@ static int fill_powernow_table(struct powernow_k8_data *data, powernow_table = kzalloc((sizeof(*powernow_table) * (data->numps + 1)), GFP_KERNEL); - if (!powernow_table) { - pr_err("powernow_table memory alloc failure\n"); + if (!powernow_table) return -ENOMEM; - } for (j = 0; j < data->numps; j++) { int freq; @@ -760,10 +756,8 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) /* fill in data->powernow_table */ powernow_table = kzalloc((sizeof(*powernow_table) * (data->acpi_data.state_count + 1)), GFP_KERNEL); - if (!powernow_table) { - pr_debug("powernow_table memory alloc failure\n"); + if (!powernow_table) goto err_out; - } /* fill in data */ data->numps = data->acpi_data.state_count; @@ -1042,10 +1036,8 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol) return -ENODEV; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - pr_err("unable to alloc powernow_k8_data\n"); + if (!data) return -ENOMEM; - } data->cpu = pol->cpu; @@ -1084,15 +1076,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol) cpumask_copy(pol->cpus, topology_core_cpumask(pol->cpu)); data->available_cores = pol->cpus; - - /* min/max the cpu is capable of */ - if (cpufreq_table_validate_and_show(pol, data->powernow_table)) { - pr_err(FW_BUG "invalid powernow_table\n"); - powernow_k8_cpu_exit_acpi(data); - kfree(data->powernow_table); - kfree(data); - return -EINVAL; - } + pol->freq_table = data->powernow_table; pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n", data->currfid, data->currvid); diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 29cdec198657..0591874856d3 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -812,7 +812,7 @@ gpstates_done: static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) { - int base, i, ret; + int base, i; struct kernfs_node *kn; struct global_pstate_info *gpstates; @@ -848,15 +848,10 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) gpstates->timer.expires = jiffies + msecs_to_jiffies(GPSTATE_TIMER_INTERVAL); spin_lock_init(&gpstates->gpstate_lock); - ret = cpufreq_table_validate_and_show(policy, powernv_freqs); - - if (ret < 0) { - kfree(policy->driver_data); - return ret; - } + policy->freq_table = powernv_freqs; policy->fast_switch_possible = true; - return ret; + return 0; } static int powernv_cpufreq_cpu_exit(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index 5a4c5a639f61..41a0f0be3f9f 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -121,9 +121,8 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu)); #endif - /* this ensures that policy->cpuinfo_min - * and policy->cpuinfo_max are set correctly */ - return cpufreq_table_validate_and_show(policy, cbe_freqs); + policy->freq_table = cbe_freqs; + return 0; } static int cbe_cpufreq_target(struct cpufreq_policy *policy, diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index 06b024a3e474..46254e583982 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -292,10 +292,10 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) pr_info("using %s frequency table\n", pxa255_turbo_table ? "turbo" : "run"); - cpufreq_table_validate_and_show(policy, pxa255_freq_table); + policy->freq_table = pxa255_freq_table; } else if (cpu_is_pxa27x()) { - cpufreq_table_validate_and_show(policy, pxa27x_freq_table); + policy->freq_table = pxa27x_freq_table; } pr_info("frequency change support initialized\n"); diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index a01275900389..7acc7fa4536d 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -108,7 +108,9 @@ static int setup_freqs_table(struct cpufreq_policy *policy, pxa3xx_freqs_num = num; pxa3xx_freqs_table = table; - return cpufreq_table_validate_and_show(policy, table); + policy->freq_table = table; + + return 0; } static void __update_core_freq(struct pxa3xx_freq_info *info) diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 0562761a3dec..3d773f64b4df 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -165,7 +165,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table, static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) { struct device_node *np; - int i, count, ret; + int i, count; u32 freq; struct clk *clk; const struct clk_hw *hwclk; @@ -192,16 +192,12 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) count = clk_hw_get_num_parents(hwclk); data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL); - if (!data->pclk) { - pr_err("%s: no memory\n", __func__); + if (!data->pclk) goto err_nomem2; - } table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); - if (!table) { - pr_err("%s: no memory\n", __func__); + if (!table) goto err_pclk; - } for (i = 0; i < count; i++) { clk = clk_hw_get_parent_by_index(hwclk, i)->clk; @@ -213,14 +209,7 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) freq_table_redup(table, count); freq_table_sort(table, count); table[i].frequency = CPUFREQ_TABLE_END; - - /* set the min and max frequency properly */ - ret = cpufreq_table_validate_and_show(policy, table); - if (ret) { - pr_err("invalid frequency table: %d\n", ret); - goto err_nomem1; - } - + policy->freq_table = table; data->table = table; /* update ->cpus if we have cluster, no harm if not */ @@ -236,8 +225,6 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) return 0; -err_nomem1: - kfree(table); err_pclk: kfree(data->pclk); err_nomem2: diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 6bebc1f9f55a..909bd6e27639 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -351,11 +351,8 @@ struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name) static int s3c_cpufreq_init(struct cpufreq_policy *policy) { policy->clk = clk_arm; - policy->cpuinfo.transition_latency = cpu_cur.info->latency; - - if (ftab) - return cpufreq_table_validate_and_show(policy, ftab); + policy->freq_table = ftab; return 0; } @@ -479,10 +476,8 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) * initdata. */ ours = kzalloc(sizeof(*ours), GFP_KERNEL); - if (ours == NULL) { - pr_err("%s: no memory\n", __func__); + if (!ours) return -ENOMEM; - } *ours = *board; cpu_cur.board = ours; @@ -568,10 +563,8 @@ static int s3c_cpufreq_build_freq(void) size++; ftab = kzalloc(sizeof(*ftab) * size, GFP_KERNEL); - if (!ftab) { - pr_err("%s: no memory for tables\n", __func__); + if (!ftab) return -ENOMEM; - } ftab_size = size; diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index 4225501a4b78..abaa75e86148 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -83,8 +83,9 @@ static int sc520_freq_cpu_init(struct cpufreq_policy *policy) /* cpuinfo and default policy values */ policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->freq_table = sc520_freq_table; - return cpufreq_table_validate_and_show(policy, sc520_freq_table); + return 0; } diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index d300a163945f..87a98ec77773 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -158,13 +158,7 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy) } policy->driver_data = priv; - - ret = cpufreq_table_validate_and_show(policy, freq_table); - if (ret) { - dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, - ret); - goto out_put_clk; - } + policy->freq_table = freq_table; /* scpi allows DVFS request for any domain from any CPU */ policy->dvfs_possible_from_any_cpu = true; @@ -178,8 +172,6 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy) policy->fast_switch_possible = false; return 0; -out_put_clk: - clk_put(priv->clk); out_free_cpufreq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_priv: diff --git a/drivers/cpufreq/sfi-cpufreq.c b/drivers/cpufreq/sfi-cpufreq.c index 3779742f86e3..9767afe05da2 100644 --- a/drivers/cpufreq/sfi-cpufreq.c +++ b/drivers/cpufreq/sfi-cpufreq.c @@ -72,8 +72,9 @@ static int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy) { policy->shared_type = CPUFREQ_SHARED_TYPE_HW; policy->cpuinfo.transition_latency = 100000; /* 100us */ + policy->freq_table = freq_table; - return cpufreq_table_validate_and_show(policy, freq_table); + return 0; } static struct cpufreq_driver sfi_cpufreq_driver = { diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 28893d435cf5..5096c0ab781b 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -122,11 +122,7 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL; if (freq_table) { - int result; - - result = cpufreq_table_validate_and_show(policy, freq_table); - if (result) - return result; + policy->freq_table = freq_table; } else { dev_notice(dev, "no frequency table found, falling back " "to rate rounding.\n"); @@ -137,11 +133,6 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; } - dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, " - "Maximum %u.%03u MHz.\n", - policy->min / 1000, policy->min % 1000, - policy->max / 1000, policy->max % 1000); - return 0; } @@ -155,6 +146,16 @@ static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy) return 0; } +static void sh_cpufreq_cpu_ready(struct cpufreq_policy *policy) +{ + struct device *dev = get_cpu_device(policy->cpu); + + dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, " + "Maximum %u.%03u MHz.\n", + policy->min / 1000, policy->min % 1000, + policy->max / 1000, policy->max % 1000); +} + static struct cpufreq_driver sh_cpufreq_driver = { .name = "sh", .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, @@ -163,6 +164,7 @@ static struct cpufreq_driver sh_cpufreq_driver = { .verify = sh_cpufreq_verify, .init = sh_cpufreq_cpu_init, .exit = sh_cpufreq_cpu_exit, + .ready = sh_cpufreq_cpu_ready, .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index 90f33efee5fc..bbd1d1e166b8 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -292,8 +292,9 @@ static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = 0; policy->cur = clock_tick; + policy->freq_table = table; - return cpufreq_table_validate_and_show(policy, table); + return 0; } static int us2e_freq_cpu_exit(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index 30645b0118f9..51e3c6018e74 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -136,8 +136,9 @@ static int __init us3_freq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = 0; policy->cur = clock_tick; + policy->freq_table = table; - return cpufreq_table_validate_and_show(policy, table); + return 0; } static int us3_freq_cpu_exit(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 4fa5adf16c70..a1fb735685db 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -394,9 +394,9 @@ static int centrino_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = 10000; /* 10uS transition latency */ + policy->freq_table = per_cpu(centrino_model, policy->cpu)->op_points; - return cpufreq_table_validate_and_show(policy, - per_cpu(centrino_model, policy->cpu)->op_points); + return 0; } static int centrino_cpu_exit(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index 0412a246a785..fbbcb88db061 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -304,7 +304,9 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) if (gf.ret) return gf.ret; - return cpufreq_table_validate_and_show(policy, speedstep_freqs); + policy->freq_table = speedstep_freqs; + + return 0; } diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index d23f24ccff38..01fe70dae20b 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -266,7 +266,9 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) pr_debug("workaround worked.\n"); } - return cpufreq_table_validate_and_show(policy, speedstep_freqs); + policy->freq_table = speedstep_freqs; + + return 0; } static unsigned int speedstep_get(unsigned int cpu) diff --git a/drivers/cpufreq/tegra186-cpufreq.c b/drivers/cpufreq/tegra186-cpufreq.c index fe7875311d62..1f59966573aa 100644 --- a/drivers/cpufreq/tegra186-cpufreq.c +++ b/drivers/cpufreq/tegra186-cpufreq.c @@ -78,7 +78,8 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy) policy->driver_data = data->regs + info->offset + EDVD_CORE_VOLT_FREQ(core); - cpufreq_table_validate_and_show(policy, cluster->table); + policy->freq_table = cluster->table; + break; } policy->cpuinfo.transition_latency = 300 * 1000; diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index ddee1b601b89..e07bc7ace774 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -129,7 +129,6 @@ static int __init arm_idle_init_cpu(int cpu) dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { - pr_err("Failed to allocate cpuidle device\n"); ret = -ENOMEM; goto out_unregister_drv; } diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c index 00cd129b10a4..0171a6e190d7 100644 --- a/drivers/cpuidle/cpuidle-exynos.c +++ b/drivers/cpuidle/cpuidle-exynos.c @@ -117,7 +117,8 @@ static int exynos_cpuidle_probe(struct platform_device *pdev) int ret; if (IS_ENABLED(CONFIG_SMP) && - of_machine_is_compatible("samsung,exynos4210")) { + (of_machine_is_compatible("samsung,exynos4210") || + of_machine_is_compatible("samsung,exynos3250"))) { exynos_cpuidle_pdata = pdev->dev.platform_data; ret = cpuidle_register(&exynos_coupled_idle_driver, diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 68a16827f45f..0003e9a02637 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -131,6 +131,10 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv, static void enter_s2idle_proper(struct cpuidle_driver *drv, struct cpuidle_device *dev, int index) { + ktime_t time_start, time_end; + + time_start = ns_to_ktime(local_clock()); + /* * trace_suspend_resume() called by tick_freeze() for the last CPU * executing it contains RCU usage regarded as invalid in the idle @@ -152,6 +156,11 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv, */ RCU_NONIDLE(tick_unfreeze()); start_critical_timings(); + + time_end = ns_to_ktime(local_clock()); + + dev->states_usage[index].s2idle_time += ktime_us_delta(time_end, time_start); + dev->states_usage[index].s2idle_usage++; } /** diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c index 7416b16287de..3f86d23c592e 100644 --- a/drivers/cpuidle/poll_state.c +++ b/drivers/cpuidle/poll_state.c @@ -6,15 +6,30 @@ #include <linux/cpuidle.h> #include <linux/sched.h> +#include <linux/sched/clock.h> #include <linux/sched/idle.h> +#define POLL_IDLE_TIME_LIMIT (TICK_NSEC / 16) +#define POLL_IDLE_RELAX_COUNT 200 + static int __cpuidle poll_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { + u64 time_start = local_clock(); + local_irq_enable(); if (!current_set_polling_and_test()) { - while (!need_resched()) + unsigned int loop_count = 0; + + while (!need_resched()) { cpu_relax(); + if (loop_count++ < POLL_IDLE_RELAX_COUNT) + continue; + + loop_count = 0; + if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT) + break; + } } current_clr_polling(); diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index ae948b1da93a..e754c7aae7f7 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -330,6 +330,58 @@ struct cpuidle_state_kobj { struct kobject kobj; }; +#ifdef CONFIG_SUSPEND +#define define_show_state_s2idle_ull_function(_name) \ +static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, \ + char *buf) \ +{ \ + return sprintf(buf, "%llu\n", state_usage->s2idle_##_name);\ +} + +define_show_state_s2idle_ull_function(usage); +define_show_state_s2idle_ull_function(time); + +#define define_one_state_s2idle_ro(_name, show) \ +static struct cpuidle_state_attr attr_s2idle_##_name = \ + __ATTR(_name, 0444, show, NULL) + +define_one_state_s2idle_ro(usage, show_state_s2idle_usage); +define_one_state_s2idle_ro(time, show_state_s2idle_time); + +static struct attribute *cpuidle_state_s2idle_attrs[] = { + &attr_s2idle_usage.attr, + &attr_s2idle_time.attr, + NULL +}; + +static const struct attribute_group cpuidle_state_s2idle_group = { + .name = "s2idle", + .attrs = cpuidle_state_s2idle_attrs, +}; + +static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) +{ + int ret; + + if (!kobj->state->enter_s2idle) + return; + + ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group); + if (ret) + pr_debug("%s: sysfs attribute group not created\n", __func__); +} + +static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) +{ + if (kobj->state->enter_s2idle) + sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group); +} +#else +static inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { } +static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { } +#endif /* CONFIG_SUSPEND */ + #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) #define kobj_to_state(k) (kobj_to_state_obj(k)->state) #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) @@ -383,6 +435,7 @@ static struct kobj_type ktype_state_cpuidle = { static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i) { + cpuidle_remove_s2idle_attr_group(device->kobjs[i]); kobject_put(&device->kobjs[i]->kobj); wait_for_completion(&device->kobjs[i]->kobj_unregister); kfree(device->kobjs[i]); @@ -417,6 +470,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) kfree(kobj); goto error_state; } + cpuidle_add_s2idle_attr_group(kobj); kobject_uevent(&kobj->kobj, KOBJ_ADD); device->kobjs[i] = kobj; } diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index c3b615c94b4b..8c8caec3a72c 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -452,17 +452,20 @@ static int socket_insert(struct pcmcia_socket *skt) static int socket_suspend(struct pcmcia_socket *skt) { - if (skt->state & SOCKET_SUSPEND) + if ((skt->state & SOCKET_SUSPEND) && !(skt->state & SOCKET_IN_RESUME)) return -EBUSY; mutex_lock(&skt->ops_mutex); - skt->suspended_state = skt->state; + /* store state on first suspend, but not after spurious wakeups */ + if (!(skt->state & SOCKET_IN_RESUME)) + skt->suspended_state = skt->state; skt->socket = dead_socket; skt->ops->set_socket(skt, &skt->socket); if (skt->ops->suspend) skt->ops->suspend(skt); skt->state |= SOCKET_SUSPEND; + skt->state &= ~SOCKET_IN_RESUME; mutex_unlock(&skt->ops_mutex); return 0; } @@ -475,6 +478,7 @@ static int socket_early_resume(struct pcmcia_socket *skt) skt->ops->set_socket(skt, &skt->socket); if (skt->state & SOCKET_PRESENT) skt->resume_status = socket_setup(skt, resume_delay); + skt->state |= SOCKET_IN_RESUME; mutex_unlock(&skt->ops_mutex); return 0; } @@ -484,7 +488,7 @@ static int socket_late_resume(struct pcmcia_socket *skt) int ret = 0; mutex_lock(&skt->ops_mutex); - skt->state &= ~SOCKET_SUSPEND; + skt->state &= ~(SOCKET_SUSPEND | SOCKET_IN_RESUME); mutex_unlock(&skt->ops_mutex); if (!(skt->state & SOCKET_PRESENT)) { diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 6765beadea95..03ec43802909 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -70,6 +70,7 @@ struct pccard_resource_ops { /* Flags in socket state */ #define SOCKET_PRESENT 0x0008 #define SOCKET_INUSE 0x0010 +#define SOCKET_IN_RESUME 0x0040 #define SOCKET_SUSPEND 0x0080 #define SOCKET_WIN_REQ(i) (0x0100<<(i)) #define SOCKET_CARDBUS 0x8000 diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 35636e1d8a3d..295d8dcba48c 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1162,6 +1162,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(INTEL_FAM6_SKYLAKE_X, rapl_defaults_hsw_server), RAPL_CPU(INTEL_FAM6_KABYLAKE_MOBILE, rapl_defaults_core), RAPL_CPU(INTEL_FAM6_KABYLAKE_DESKTOP, rapl_defaults_core), + RAPL_CPU(INTEL_FAM6_CANNONLAKE_MOBILE, rapl_defaults_core), RAPL_CPU(INTEL_FAM6_ATOM_SILVERMONT1, rapl_defaults_byt), RAPL_CPU(INTEL_FAM6_ATOM_AIRMONT, rapl_defaults_cht), diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 21e8d248d956..1fe49724da9e 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -962,6 +962,7 @@ extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs; extern struct freq_attr *cpufreq_generic_attr[]; int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table); +int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy); unsigned int cpufreq_generic_get(unsigned int cpu); int cpufreq_generic_init(struct cpufreq_policy *policy, diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 0b3fc229086c..a806e94c482f 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -33,6 +33,10 @@ struct cpuidle_state_usage { unsigned long long disable; unsigned long long usage; unsigned long long time; /* in US */ +#ifdef CONFIG_SUSPEND + unsigned long long s2idle_usage; + unsigned long long s2idle_time; /* in US */ +#endif }; struct cpuidle_state { diff --git a/include/linux/device.h b/include/linux/device.h index b093405ed525..abf952c82c6d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -769,6 +769,7 @@ enum device_link_state { * @status: The state of the link (with respect to the presence of drivers). * @flags: Link flags. * @rpm_active: Whether or not the consumer device is runtime-PM-active. + * @kref: Count repeated addition of the same link. * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks. */ struct device_link { @@ -779,6 +780,7 @@ struct device_link { enum device_link_state status; u32 flags; bool rpm_active; + struct kref kref; #ifdef CONFIG_SRCU struct rcu_head rcu_head; #endif diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 4710f1b142fc..5454cc639a8d 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1053,7 +1053,7 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, lock_system_sleep(); swsusp_resume_device = res; unlock_system_sleep(); - pr_info("Starting manual resume from disk\n"); + pm_pr_dbg("Configured resume from disk to %u\n", swsusp_resume_device); noresume = 0; software_resume(); return n; @@ -1061,6 +1061,29 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, power_attr(resume); +static ssize_t resume_offset_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%llu\n", (unsigned long long)swsusp_resume_block); +} + +static ssize_t resume_offset_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t n) +{ + unsigned long long offset; + int rc; + + rc = kstrtoull(buf, 0, &offset); + if (rc) + return rc; + swsusp_resume_block = offset; + + return n; +} + +power_attr(resume_offset); + static ssize_t image_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -1106,6 +1129,7 @@ power_attr(reserved_size); static struct attribute * g[] = { &disk_attr.attr, + &resume_offset_attr.attr, &resume_attr.attr, &image_size_attr.attr, &reserved_size_attr.attr, diff --git a/tools/power/pm-graph/Makefile b/tools/power/pm-graph/Makefile index 4e1e999e7b05..c1899cd72c80 100644 --- a/tools/power/pm-graph/Makefile +++ b/tools/power/pm-graph/Makefile @@ -7,11 +7,24 @@ all: install : uninstall install -d $(DESTDIR)$(PREFIX)/lib/pm-graph - install analyze_suspend.py $(DESTDIR)$(PREFIX)/lib/pm-graph - install analyze_boot.py $(DESTDIR)$(PREFIX)/lib/pm-graph + install sleepgraph.py $(DESTDIR)$(PREFIX)/lib/pm-graph + install bootgraph.py $(DESTDIR)$(PREFIX)/lib/pm-graph + install -d $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/cgskip.txt $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/freeze-callgraph.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/freeze.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/freeze-dev.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/standby-callgraph.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/standby.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/standby-dev.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/suspend-callgraph.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/suspend.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/suspend-dev.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config + install -m 644 config/suspend-x2-proc.cfg $(DESTDIR)$(PREFIX)/lib/pm-graph/config - ln -s $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py $(DESTDIR)$(PREFIX)/bin/bootgraph - ln -s $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py $(DESTDIR)$(PREFIX)/bin/sleepgraph + install -d $(DESTDIR)$(PREFIX)/bin + ln -s $(DESTDIR)$(PREFIX)/lib/pm-graph/bootgraph.py $(DESTDIR)$(PREFIX)/bin/bootgraph + ln -s $(DESTDIR)$(PREFIX)/lib/pm-graph/sleepgraph.py $(DESTDIR)$(PREFIX)/bin/sleepgraph install -d $(DESTDIR)$(PREFIX)/share/man/man8 install bootgraph.8 $(DESTDIR)$(PREFIX)/share/man/man8 @@ -24,9 +37,11 @@ uninstall : rm -f $(DESTDIR)$(PREFIX)/bin/bootgraph rm -f $(DESTDIR)$(PREFIX)/bin/sleepgraph - rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py - rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py - rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/*.pyc + rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/config/* + if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph/config ] ; then \ + rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph/config; \ + fi; + rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/* if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph ] ; then \ rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph; \ fi; diff --git a/tools/power/pm-graph/bootgraph.8 b/tools/power/pm-graph/bootgraph.8 index dbdafcf546df..64d513f80a2a 100644 --- a/tools/power/pm-graph/bootgraph.8 +++ b/tools/power/pm-graph/bootgraph.8 @@ -37,6 +37,9 @@ Print the current tool version Add the dmesg log to the html output. It will be viewable by clicking a button in the timeline. .TP +\fB-result \fIfile\fR +Export a results table to a text file for parsing. +.TP \fB-o \fIname\fR Overrides the output subdirectory name when running a new test. Use {date}, {time}, {hostname} for current values. @@ -44,14 +47,14 @@ Use {date}, {time}, {hostname} for current values. e.g. boot-{hostname}-{date}-{time} .SS "advanced" .TP -\fB-f\fR -Use ftrace to add function detail (default: disabled) -.TP -\fB-callgraph\fR +\fB-f or -callgraph\fR Use ftrace to create initcall callgraphs (default: disabled). If -func is not used there will be one callgraph per initcall. This can produce very large outputs, i.e. 10MB - 100MB. .TP +\fB-fstat\fR +Use ftrace to add function detail (default: disabled) +.TP \fB-maxdepth \fIlevel\fR limit the callgraph trace depth to \fIlevel\fR (default: 2). This is the best way to limit the output size when using -callgraph. @@ -67,6 +70,13 @@ Reduce callgraph output in the timeline by limiting it to a list of calls. The argument can be a single function name or a comma delimited list. (default: none) .TP +\fB-cgskip \fIfile\fR +Reduce callgraph output in the timeline by skipping over uninteresting +functions in the trace, e.g. printk or console_unlock. The functions listed +in this file will show up as empty leaves in the callgraph with only the start/end +times displayed. +(default: none) +.TP \fB-timeprec \fIn\fR Number of significant digits in timestamps (0:S, 3:ms, [6:us]) .TP diff --git a/tools/power/pm-graph/analyze_boot.py b/tools/power/pm-graph/bootgraph.py index e83df141a597..abb4c38f029b 100755 --- a/tools/power/pm-graph/analyze_boot.py +++ b/tools/power/pm-graph/bootgraph.py @@ -32,7 +32,7 @@ import platform import shutil from datetime import datetime, timedelta from subprocess import call, Popen, PIPE -import analyze_suspend as aslib +import sleepgraph as aslib # ----------------- CLASSES -------------------- @@ -42,23 +42,18 @@ import analyze_suspend as aslib # store system values and test parameters class SystemValues(aslib.SystemValues): title = 'BootGraph' - version = '2.1' + version = '2.2' hostname = 'localhost' testtime = '' kernel = '' dmesgfile = '' ftracefile = '' htmlfile = 'bootgraph.html' - outfile = '' testdir = '' - testdirprefix = 'boot' - embedded = False - testlog = False - dmesglog = False - ftracelog = False + kparams = '' + result = '' useftrace = False usecallgraph = False - usedevsrc = True suspendmode = 'boot' max_graph_depth = 2 graph_filter = 'do_one_initcall' @@ -69,11 +64,6 @@ class SystemValues(aslib.SystemValues): bootloader = 'grub' blexec = [] def __init__(self): - if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): - self.embedded = True - self.dmesglog = True - self.outfile = os.environ['LOG_FILE'] - self.htmlfile = os.environ['LOG_FILE'] self.hostname = platform.node() self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') if os.path.exists('/proc/version'): @@ -148,11 +138,18 @@ class SystemValues(aslib.SystemValues): cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0]) args = iter(sys.argv[1:]) for arg in args: - if arg in ['-h', '-v', '-cronjob', '-reboot']: + if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']: continue elif arg in ['-o', '-dmesg', '-ftrace', '-func']: args.next() continue + elif arg == '-result': + cmdline += ' %s "%s"' % (arg, os.path.abspath(args.next())) + continue + elif arg == '-cgskip': + file = self.configFile(args.next()) + cmdline += ' %s "%s"' % (arg, os.path.abspath(file)) + continue cmdline += ' '+arg if self.graph_filter != 'do_one_initcall': cmdline += ' -func "%s"' % self.graph_filter @@ -166,14 +163,6 @@ class SystemValues(aslib.SystemValues): print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n' print 'CMDLINE="%s"' % cmdline sys.exit() - def getExec(self, cmd): - dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', - '/usr/local/sbin', '/usr/local/bin'] - for path in dirlist: - cmdfull = os.path.join(path, cmd) - if os.path.exists(cmdfull): - return cmdfull - return '' def blGrub(self): blcmd = '' for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']: @@ -199,6 +188,14 @@ class SystemValues(aslib.SystemValues): self.blGrub() else: doError('unknown boot loader: %s' % self.bootloader) + def writeDatafileHeader(self, filename): + self.kparams = open('/proc/cmdline', 'r').read().strip() + fp = open(filename, 'w') + fp.write(self.teststamp+'\n') + fp.write(self.sysstamp+'\n') + fp.write('# command | %s\n' % self.cmdline) + fp.write('# kparams | %s\n' % self.kparams) + fp.close() sysvals = SystemValues() @@ -249,7 +246,7 @@ class Data(aslib.Data): return name def deviceMatch(self, pid, cg): if cg.end - cg.start == 0: - return True + return '' for p in data.phases: list = self.dmesg[p]['list'] for devname in list: @@ -260,14 +257,25 @@ class Data(aslib.Data): if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0): dev['ftrace'] = cg self.do_one_initcall = True - return True + return devname else: if(cg.start > dev['start'] and cg.end < dev['end']): if 'ftraces' not in dev: dev['ftraces'] = [] dev['ftraces'].append(cg) - return True - return False + return devname + return '' + def printDetails(self): + sysvals.vprint('Timeline Details:') + sysvals.vprint(' Host: %s' % sysvals.hostname) + sysvals.vprint(' Kernel: %s' % sysvals.kernel) + sysvals.vprint(' Test time: %s' % sysvals.testtime) + sysvals.vprint(' Boot time: %s' % self.boottime) + for phase in self.phases: + dc = len(self.dmesg[phase]['list']) + sysvals.vprint('%9s mode: %.3f - %.3f (%d initcalls)' % (phase, + self.dmesg[phase]['start']*1000, + self.dmesg[phase]['end']*1000, dc)) # ----------------- FUNCTIONS -------------------- @@ -275,6 +283,8 @@ class Data(aslib.Data): # Description: # parse a kernel log for boot data def parseKernelLog(): + sysvals.vprint('Analyzing the dmesg data (%s)...' % \ + os.path.basename(sysvals.dmesgfile)) phase = 'kernel' data = Data(0) data.dmesg['kernel']['start'] = data.start = ktime = 0.0 @@ -298,6 +308,12 @@ def parseKernelLog(): elif re.match(tp.sysinfofmt, line): tp.sysinfo = line continue + elif re.match(tp.cmdlinefmt, line): + tp.cmdline = line + continue + elif re.match(tp.kparamsfmt, line): + tp.kparams = line + continue idx = line.find('[') if idx > 1: line = line[idx:] @@ -353,6 +369,17 @@ def parseKernelLog(): # Description: # Check if trace is available and copy to a temp file def parseTraceLog(data): + sysvals.vprint('Analyzing the ftrace data (%s)...' % \ + os.path.basename(sysvals.ftracefile)) + # if available, calculate cgfilter allowable ranges + cgfilter = [] + if len(sysvals.cgfilter) > 0: + for p in data.phases: + list = data.dmesg[p]['list'] + for i in sysvals.cgfilter: + if i in list: + cgfilter.append([list[i]['start']-0.0001, + list[i]['end']+0.0001]) # parse the trace log ftemp = dict() tp = aslib.TestProps() @@ -366,7 +393,16 @@ def parseTraceLog(data): continue m_time, m_proc, m_pid, m_msg, m_dur = \ m.group('time', 'proc', 'pid', 'msg', 'dur') - if float(m_time) > data.end: + t = float(m_time) + if len(cgfilter) > 0: + allow = False + for r in cgfilter: + if t >= r[0] and t < r[1]: + allow = True + break + if not allow: + continue + if t > data.end: break if(m_time and m_pid and m_msg): t = aslib.FTraceLine(m_time, m_msg, m_dur) @@ -378,24 +414,36 @@ def parseTraceLog(data): key = (m_proc, pid) if(key not in ftemp): ftemp[key] = [] - ftemp[key].append(aslib.FTraceCallGraph(pid)) + ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals)) cg = ftemp[key][-1] - if(cg.addLine(t)): - ftemp[key].append(aslib.FTraceCallGraph(pid)) + res = cg.addLine(t) + if(res != 0): + ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals)) + if(res == -1): + ftemp[key][-1].addLine(t) + tf.close() # add the callgraph data to the device hierarchy for key in ftemp: proc, pid = key for cg in ftemp[key]: - if len(cg.list) < 1 or cg.invalid: + if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0): continue if(not cg.postProcess()): print('Sanity check failed for %s-%d' % (proc, pid)) continue # match cg data to devices - if not data.deviceMatch(pid, cg): - print ' BAD: %s %s-%d [%f - %f]' % (cg.name, proc, pid, cg.start, cg.end) + devname = data.deviceMatch(pid, cg) + if not devname: + kind = 'Orphan' + if cg.partial: + kind = 'Partial' + sysvals.vprint('%s callgraph found for %s %s-%d [%f - %f]' %\ + (kind, cg.name, proc, pid, cg.start, cg.end)) + elif len(cg.list) > 1000000: + print 'WARNING: the callgraph found for %s is massive! (%d lines)' %\ + (devname, len(cg.list)) # Function: retrieveLogs # Description: @@ -473,7 +521,7 @@ def createBootGraph(data): devtl = aslib.Timeline(100, 20) # write the test title and general info header - devtl.createHeader(sysvals) + devtl.createHeader(sysvals, sysvals.stamp) # Generate the header for this timeline t0 = data.start @@ -574,12 +622,9 @@ def createBootGraph(data): data.dmesg[phase]['color'], phase+'_mode', phase[0]) devtl.html += '</div>\n' - if(sysvals.outfile == sysvals.htmlfile): - hf = open(sysvals.htmlfile, 'a') - else: - hf = open(sysvals.htmlfile, 'w') + hf = open(sysvals.htmlfile, 'w') - # add the css if this is not an embedded run + # add the css extra = '\ .c1 {background:rgba(209,0,0,0.4);}\n\ .c2 {background:rgba(255,102,34,0.4);}\n\ @@ -597,8 +642,7 @@ def createBootGraph(data): .fstat td {text-align:left;width:35px;}\n\ .srccall {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\ .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n' - if(not sysvals.embedded): - aslib.addCSS(hf, sysvals, 1, False, extra) + aslib.addCSS(hf, sysvals, 1, False, extra) # write the device timeline hf.write(devtl.html) @@ -631,6 +675,9 @@ def createBootGraph(data): if(sysvals.usecallgraph): aslib.addCallgraphs(sysvals, hf, data) + # add the test log as a hidden div + if sysvals.testlog and sysvals.logmsg: + hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n') # add the dmesg log as a hidden div if sysvals.dmesglog: hf.write('<div id="dmesglog" style="display:none;">\n') @@ -639,14 +686,9 @@ def createBootGraph(data): hf.write(line) hf.write('</div>\n') - if(not sysvals.embedded): - # write the footer and close - aslib.addScriptCode(hf, [data]) - hf.write('</body>\n</html>\n') - else: - # embedded out will be loaded in a page, skip the js - hf.write('<div id=bounds style=display:none>%f,%f</div>' % \ - (data.start*1000, data.end*1000)) + # write the footer and close + aslib.addScriptCode(hf, [data]) + hf.write('</body>\n</html>\n') hf.close() return True @@ -780,6 +822,7 @@ def doError(msg, help=False): if help == True: printHelp() print 'ERROR: %s\n' % msg + sysvals.outputResult({'error':msg}) sys.exit() # Function: printHelp @@ -806,18 +849,21 @@ def printHelp(): print('Options:') print(' -h Print this help text') print(' -v Print the current tool version') + print(' -verbose Print extra information during execution and analysis') print(' -addlogs Add the dmesg log to the html output') + print(' -result fn Export a results table to a text file for parsing.') print(' -o name Overrides the output subdirectory name when running a new test') print(' default: boot-{date}-{time}') print(' [advanced]') - print(' -f Use ftrace to add function detail (default: disabled)') - print(' -callgraph Add callgraph detail, can be very large (default: disabled)') + print(' -fstat Use ftrace to add function detail and statistics (default: disabled)') + print(' -f/-callgraph Add callgraph detail, can be very large (default: disabled)') print(' -maxdepth N limit the callgraph data to N call levels (default: 2)') print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') print(' -timeprec N Number of significant digits in timestamps (0:S, 3:ms, [6:us])') print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') print(' -func list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)') print(' -cgfilter S Filter the callgraph output in the timeline') + print(' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)') print(' -bl name Use the following boot loader for kernel params (default: grub)') print(' -reboot Reboot the machine automatically and generate a new timeline') print(' -manual Show the steps to generate a new timeline manually (used with -reboot)') @@ -837,8 +883,13 @@ if __name__ == '__main__': # loop through the command line arguments cmd = '' testrun = True + switchoff = ['disable', 'off', 'false', '0'] simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl'] + cgskip = '' + if '-f' in sys.argv: + cgskip = sysvals.configFile('cgskip.txt') args = iter(sys.argv[1:]) + mdset = False for arg in args: if(arg == '-h'): printHelp() @@ -846,13 +897,17 @@ if __name__ == '__main__': elif(arg == '-v'): print("Version %s" % sysvals.version) sys.exit() + elif(arg == '-verbose'): + sysvals.verbose = True elif(arg in simplecmds): cmd = arg[1:] - elif(arg == '-f'): + elif(arg == '-fstat'): sysvals.useftrace = True - elif(arg == '-callgraph'): + elif(arg == '-callgraph' or arg == '-f'): sysvals.useftrace = True sysvals.usecallgraph = True + elif(arg == '-cgdump'): + sysvals.cgdump = True elif(arg == '-mincg'): sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0) elif(arg == '-cgfilter'): @@ -860,7 +915,18 @@ if __name__ == '__main__': val = args.next() except: doError('No callgraph functions supplied', True) - sysvals.setDeviceFilter(val) + sysvals.setCallgraphFilter(val) + elif(arg == '-cgskip'): + try: + val = args.next() + except: + doError('No file supplied', True) + if val.lower() in switchoff: + cgskip = '' + else: + cgskip = sysvals.configFile(val) + if(not cgskip): + doError('%s does not exist' % cgskip) elif(arg == '-bl'): try: val = args.next() @@ -872,6 +938,7 @@ if __name__ == '__main__': elif(arg == '-timeprec'): sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6)) elif(arg == '-maxdepth'): + mdset = True sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000) elif(arg == '-func'): try: @@ -902,8 +969,6 @@ if __name__ == '__main__': doError('No dmesg file supplied', True) if(os.path.exists(val) == False): doError('%s does not exist' % val) - if(sysvals.htmlfile == val or sysvals.outfile == val): - doError('Output filename collision') testrun = False sysvals.dmesgfile = val elif(arg == '-o'): @@ -912,6 +977,12 @@ if __name__ == '__main__': except: doError('No subdirectory name supplied', True) sysvals.testdir = sysvals.setOutputFolder(val) + elif(arg == '-result'): + try: + val = args.next() + except: + doError('No result file supplied', True) + sysvals.result = val elif(arg == '-reboot'): sysvals.reboot = True elif(arg == '-manual'): @@ -947,7 +1018,7 @@ if __name__ == '__main__': sysvals.getBootLoader() print 'Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec) elif(cmd == 'sysinfo'): - sysvals.printSystemInfo() + sysvals.printSystemInfo(True) sys.exit() # reboot: update grub, setup a cronjob, and reboot @@ -963,6 +1034,10 @@ if __name__ == '__main__': sysvals.manualRebootRequired() sys.exit() + if sysvals.usecallgraph and cgskip: + sysvals.vprint('Using cgskip file: %s' % cgskip) + sysvals.setCallgraphBlacklist(cgskip) + # cronjob: remove the cronjob, grub changes, and disable ftrace if sysvals.iscronjob: updateCron(True) @@ -980,29 +1055,23 @@ if __name__ == '__main__': # process the log data if sysvals.dmesgfile: + if not mdset: + sysvals.max_graph_depth = 0 data = parseKernelLog() if(not data.valid): doError('No initcall data found in %s' % sysvals.dmesgfile) if sysvals.useftrace and sysvals.ftracefile: parseTraceLog(data) + if sysvals.cgdump: + data.debugPrint() + sys.exit() else: doError('dmesg file required') - print(' Host: %s' % sysvals.hostname) - print(' Test time: %s' % sysvals.testtime) - print(' Boot time: %s' % data.boottime) - print('Kernel Version: %s' % sysvals.kernel) - print(' Kernel start: %.3f' % (data.start * 1000)) - print('Usermode start: %.3f' % (data.tUserMode * 1000)) - print('Last Init Call: %.3f' % (data.end * 1000)) - - # handle embedded output logs - if(sysvals.outfile and sysvals.embedded): - fp = open(sysvals.outfile, 'w') - fp.write('pass %s initstart %.3f end %.3f boot %s\n' % - (data.valid, data.tUserMode*1000, data.end*1000, data.boottime)) - fp.close() - + sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) + sysvals.vprint('Command:\n %s' % sysvals.cmdline) + sysvals.vprint('Kernel parameters:\n %s' % sysvals.kparams) + data.printDetails() createBootGraph(data) # if running as root, change output dir owner to sudo_user @@ -1010,3 +1079,7 @@ if __name__ == '__main__': os.getuid() == 0 and 'SUDO_USER' in os.environ: cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) + + sysvals.stamp['boot'] = (data.tUserMode - data.start) * 1000 + sysvals.stamp['lastinit'] = data.end * 1000 + sysvals.outputResult(sysvals.stamp) diff --git a/tools/power/pm-graph/config/cgskip.txt b/tools/power/pm-graph/config/cgskip.txt new file mode 100644 index 000000000000..e48d588fbfb4 --- /dev/null +++ b/tools/power/pm-graph/config/cgskip.txt @@ -0,0 +1,65 @@ +# ----------------------------------------------- +# CallGraph function skip list +# +# This file contains a list of functions which are +# meant to be skipped in the callgraph trace. It reduces +# the callgraph html file size by treating these functions +# as leaves with no child calls. It can be editted by +# adding or removing function symbol names. +# +# The sleepgraph tool automatically pulls this file in when +# it is found in the config folder. It can be ignored if +# the tool is called with "-cgskip off". +# ----------------------------------------------- + +# low level scheduling and timing +up +down_timeout +mutex_lock +down_read +complete_all +schedule_timeout +wake_up_process +msleep +__udelay +ktime_get + +# console calls +printk +dev_printk +console_unlock + +# memory handling +__kmalloc +__kmalloc_track_caller +kmem_cache_alloc +kmem_cache_alloc_trace +kmem_cache_free +kstrdup +kstrdup_const +kmalloc_slab +new_slab +__slab_alloc +__slab_free +raw_pci_read +pci_read +alloc_pages_current + +# debugfs and sysfs setup +debugfs_remove_recursive +debugfs_create_dir +debugfs_create_files +debugfs_create_dir +debugfs_get_inode +sysfs_add_file_mode_ns +sysfs_add_file +sysfs_create_dir_ns +sysfs_create_link +sysfs_create_group +sysfs_create_groups +sysfs_create_bin_file +dpm_sysfs_add +sysfs_create_file_ns +sysfs_merge_group +sysfs_add_link_to_group +sysfs_create_link_sd diff --git a/tools/power/pm-graph/config/custom-timeline-functions.cfg b/tools/power/pm-graph/config/custom-timeline-functions.cfg new file mode 100644 index 000000000000..4f80ad7d7275 --- /dev/null +++ b/tools/power/pm-graph/config/custom-timeline-functions.cfg @@ -0,0 +1,205 @@ +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config thisfile.txt +# + +[Settings] + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: mem + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: true + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: suspend-{hostname}-{date}-{time}-custom + +# Override default timeline entries +# Do not use the internal default functions for timeline entries (default: false) +# Set this to true if you intend to only use the ones defined in this config +override-timeline-functions: true + +# Override default dev timeline entries +# Do not use the internal default functions for dev timeline entries (default: false) +# Set this to true if you intend to only use the ones defined in this config +override-dev-timeline-functions: true + +[timeline_functions_x86_64] +# +# Function calls to display in the timeline alongside device callbacks. +# The tool has an internal set of these functions which should cover the +# whole of kernel execution, but you can append or override here. +# +# This is a list of kprobes which use both symbol data and function arg data. +# The function calls are displayed on the timeline alongside the device blocks. +# The args are pulled directly from the stack using this architecture's registers +# and stack formatting. Three pieces of info are required. The function name, +# a format string, and an argument list +# +# Entry format: +# +# function: format{fn_arg1}_{fn_arg2} fn_arg1 fn_arg2 ... [color=purple] +# +# Required Arguments: +# +# function: The symbol name for the function you want probed, this is the +# minimum required for an entry, it will show up as the function +# name with no arguments. +# +# example: _cpu_up: +# +# Optional Arguments: +# +# format: The format to display the data on the timeline in. Use braces to +# enclose the arg names. +# +# example: CPU_ON[{cpu}] +# +# color: The color of the entry block in the timeline. The default color is +# transparent, so the entry shares the phase color. The color is an +# html color string, either a word, or an RGB. +# +# example: [color=#CC00CC] +# +# arglist: A list of arguments from registers/stack addresses. See URL: +# https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt +# +# example: cpu=%di:s32 +# +# Example: Display cpu resume in the timeline +# +# _cpu_up: CPU_ON[{cpu}] cpu=%di:s32 [color=orange] +# +_cpu_down: CPU_OFF[{cpu}] cpu=%di:s32 +_cpu_up: CPU_ON[{cpu}] cpu=%di:s32 +sys_sync: +pm_prepare_console: +pm_notifier_call_chain: +freeze_processes: +freeze_kernel_threads: +pm_restrict_gfp_mask: +acpi_suspend_begin: +suspend_console: +acpi_pm_prepare: +syscore_suspend: +arch_enable_nonboot_cpus_end: +syscore_resume: +acpi_pm_finish: +resume_console: +acpi_pm_end: +pm_restore_gfp_mask: +thaw_processes: +pm_restore_console: + +[dev_timeline_functions_x86_64] +# +# Dev mode function calls to display inside timeline entries +# +# This is a list of kprobes which use both symbol data and function arg data. +# The function calls are displayed on the timeline alongside the device blocks. +# The args are pulled directly from the stack using this architecture's registers +# and stack formatting. Three pieces of info are required. The function name, +# a format string, and an argument list +# +# Entry format: +# +# function: format{fn_arg1}_{fn_arg2} fn_arg1 fn_arg2 ... [color=purple] +# +# Required Arguments: +# +# function: The symbol name for the function you want probed, this is the +# minimum required for an entry, it will show up as the function +# name with no arguments. +# +# example: ata_eh_recover: +# +# Optional Arguments: +# +# format: The format to display the data on the timeline in. Use braces to +# enclose the arg names. +# +# example: ata{port}_port_reset +# +# color: The color of the entry block in the timeline. The default color is +# transparent, so the entry shares the phase color. The color is an +# html color string, either a word, or an RGB. +# +# example: [color=#CC00CC] +# +# arglist: A list of arguments from registers/stack addresses. See URL: +# https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt +# +# example: port=+36(%di):s32 +# +# Example: Display ATA port reset as ataN_port_reset in the timeline +# +# ata_eh_recover: ata{port}_port_reset port=+36(%di):s32 +# +msleep: msleep time=%di:s32 +schedule_timeout_uninterruptible: schedule_timeout_uninterruptible timeout=%di:s32 +schedule_timeout: schedule_timeout timeout=%di:s32 +usleep_range: usleep_range min=%di:s32 max=%si:s32 +__const_udelay: udelay loops=%di:s32 +__mutex_lock_slowpath: mutex_lock_slowpath +ata_eh_recover: ata_eh_recover port=+36(%di):s32 +acpi_os_stall: +acpi_resume_power_resources: +acpi_ps_parse_aml: +ext4_sync_fs: +i915_gem_resume: +i915_restore_state: +intel_opregion_setup: +g4x_pre_enable_dp: +vlv_pre_enable_dp: +chv_pre_enable_dp: +g4x_enable_dp: +vlv_enable_dp: +intel_hpd_init: +intel_opregion_register: +intel_dp_detect: +intel_hdmi_detect: +intel_opregion_init: +intel_fbdev_set_suspend: diff --git a/tools/power/pm-graph/config/example.cfg b/tools/power/pm-graph/config/example.cfg new file mode 100644 index 000000000000..05b2efb9bb54 --- /dev/null +++ b/tools/power/pm-graph/config/example.cfg @@ -0,0 +1,133 @@ +# +# Generic S3 (Suspend to Mem) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/example.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: mem + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: suspend-{hostname}-{date}-{time} + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: true + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# Skip HTML generation +# Only capture the logs, don't generate the html timeline (default: false) +skiphtml: false + +# Sync filesystem before suspend +# run sync before the test, minimizes sys_sync call time (default: false) +sync: true + +# Runtime suspend enable/disable +# Enable/disable runtime suspend for all devices, restore all after test (default: no-action) +# rs: disable + +# Turn display on/off for test +# Switch the display on/off for the test using xset (default: no-action) +# display: on + +# Print results to text file +# Print the status of the test run in the given file (default: no-action) +result: result.txt + +# Gzip the log files to save space +# Gzip the generated log files, and read gzipped log files (default: false) +gzip: true + +# ---- Advanced Options ---- + +# Command to execute in lieu of suspend (default: "") +# command: echo mem > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Multiple test runs +# Run N tests D seconds apart, generates separate outputs with a summary (default: false) +# multi: 3 5 + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back and display in the same timeline (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 0.001 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Callgraph phase filter +# Only enable callgraphs for one phase, i.e. resume_noirq (default: all) +cgphase: suspend + +# Callgraph x2 test filter +# Only enable callgraphs test 0 or 1 when using -x2 (default: 1) +cgtest: 0 + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 6 + +# Device Filter +# show only devices whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb + +# Add kprobe functions to the timeline +# Add functions to the timeline from a text file (default: no-action) +# fadd: file.txt diff --git a/tools/power/pm-graph/config/freeze-callgraph.cfg b/tools/power/pm-graph/config/freeze-callgraph.cfg new file mode 100644 index 000000000000..f692821c4828 --- /dev/null +++ b/tools/power/pm-graph/config/freeze-callgraph.cfg @@ -0,0 +1,94 @@ +# +# Full Callgraph for S2 (Freeze) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/freeze-callgraph.cfg +# +# NOTE: the output of this test is very large (> 30MB) + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: freeze + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: freeze-{hostname}-{date}-{time}-cg + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of freeze (default: "") +# command: echo freeze > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: true + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 6 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/freeze-dev.cfg b/tools/power/pm-graph/config/freeze-dev.cfg new file mode 100644 index 000000000000..c4ad5cea3cbf --- /dev/null +++ b/tools/power/pm-graph/config/freeze-dev.cfg @@ -0,0 +1,93 @@ +# +# Dev S2 (Freeze) test - includes src calls / kernel threads +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/freeze-dev.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: freeze + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: freeze-{hostname}-{date}-{time}-dev + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of freeze (default: "") +# command: echo freeze > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: true + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/freeze.cfg b/tools/power/pm-graph/config/freeze.cfg new file mode 100644 index 000000000000..0b70e0b74527 --- /dev/null +++ b/tools/power/pm-graph/config/freeze.cfg @@ -0,0 +1,93 @@ +# +# Generic S2 (Freeze) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/freeze.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: freeze + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: freeze-{hostname}-{date}-{time} + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of freeze (default: "") +# command: echo freeze > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 0.001 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/standby-callgraph.cfg b/tools/power/pm-graph/config/standby-callgraph.cfg new file mode 100644 index 000000000000..f52a6b9d5c32 --- /dev/null +++ b/tools/power/pm-graph/config/standby-callgraph.cfg @@ -0,0 +1,94 @@ +# +# Full Callgraph for S1 (Standby) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/standby-callgraph.cfg +# +# NOTE: the output of this test is very large (> 30MB) + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: standby + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: standby-{hostname}-{date}-{time}-cg + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of standby (default: "") +# command: echo standby > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: true + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 6 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/standby-dev.cfg b/tools/power/pm-graph/config/standby-dev.cfg new file mode 100644 index 000000000000..a5498ece3def --- /dev/null +++ b/tools/power/pm-graph/config/standby-dev.cfg @@ -0,0 +1,93 @@ +# +# Dev S1 (Standby) test - includes src calls / kernel threads +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/standby-dev.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: standby + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: standby-{hostname}-{date}-{time}-dev + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of standby (default: "") +# command: echo standby > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: true + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/standby.cfg b/tools/power/pm-graph/config/standby.cfg new file mode 100644 index 000000000000..f0dd264dfeff --- /dev/null +++ b/tools/power/pm-graph/config/standby.cfg @@ -0,0 +1,93 @@ +# +# Generic S1 (Standby) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/standby.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: standby + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: standby-{hostname}-{date}-{time} + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of standby (default: "") +# command: echo standby > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 0.001 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/suspend-callgraph.cfg b/tools/power/pm-graph/config/suspend-callgraph.cfg new file mode 100644 index 000000000000..11b8cbc12d34 --- /dev/null +++ b/tools/power/pm-graph/config/suspend-callgraph.cfg @@ -0,0 +1,98 @@ +# +# Full Callgraph for S3 (Suspend to Mem) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/suspend.cfg +# +# NOTE: the output of this test is very large (> 30MB) + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: mem + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: suspend-{hostname}-{date}-{time}-cg + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of suspend (default: "") +# command: echo mem > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 0.001 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: true + +# Max graph depth +# limit the callgraph trace to this depth (default: 0 = all) +maxdepth: 5 + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 6 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/suspend-dev.cfg b/tools/power/pm-graph/config/suspend-dev.cfg new file mode 100644 index 000000000000..56f1d21cc79b --- /dev/null +++ b/tools/power/pm-graph/config/suspend-dev.cfg @@ -0,0 +1,93 @@ +# +# Dev S3 (Suspend to Mem) test - includes src calls / kernel threads +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/suspend-dev.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: mem + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: suspend-{hostname}-{date}-{time}-dev + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of suspend (default: "") +# command: echo mem > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: true + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/suspend-x2-proc.cfg b/tools/power/pm-graph/config/suspend-x2-proc.cfg new file mode 100644 index 000000000000..0ecca0ede138 --- /dev/null +++ b/tools/power/pm-graph/config/suspend-x2-proc.cfg @@ -0,0 +1,93 @@ +# +# Proc S3 (Suspend to Mem) x2 test - includes user processes +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/suspend-proc.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: mem + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: suspend-{hostname}-{date}-{time}-x2-proc + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of suspend (default: "") +# command: echo mem > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: true + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: true + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 1000 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 1000 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 1000 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 1 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/config/suspend.cfg b/tools/power/pm-graph/config/suspend.cfg new file mode 100644 index 000000000000..70d293231b06 --- /dev/null +++ b/tools/power/pm-graph/config/suspend.cfg @@ -0,0 +1,93 @@ +# +# Generic S3 (Suspend to Mem) test +# +# This is the configuration file for sleepgraph. It contains +# all the tool arguments so that they don't have to be given on the +# command line. It also includes advanced settings for functions +# and kprobes. It is run like this +# +# sudo ./sleepgraph.py -config config/suspend.cfg +# + +[Settings] + +# ---- General Options ---- + +# Verbosity +# print verbose messages (default: false) +verbose: false + +# Suspend Mode +# e.g. standby, mem, freeze, disk (default: mem) +mode: mem + +# Output Directory Format +# output folder for html, ftrace, and dmesg. Use {date} and {time} for current values +output-dir: suspend-{hostname}-{date}-{time} + +# Automatic Wakeup +# Use rtcwake to autoresume after X seconds, or off to disable (default: 15) +rtcwake: 15 + +# Add Logs +# add the dmesg and ftrace log to the html output (default: false) +addlogs: false + +# Suspend/Resume Gap +# insert a small visible gap between suspend and resume on the timeline (default: false) +srgap: false + +# ---- Advanced Options ---- + +# Command to execute in lieu of suspend (default: "") +# command: echo mem > /sys/power/state + +# Display user processes +# graph user processes and cpu usage in the timeline (default: false) +proc: false + +# Display function calls +# graph source functions in the timeline (default: false) +dev: false + +# Back to Back Suspend/Resume +# Run two suspend/resumes back to back (default: false) +x2: false + +# Back to Back Suspend Delay +# Time delay between the two test runs in ms (default: 0 ms) +x2delay: 0 + +# Pre Suspend Delay +# Include an N ms delay before (1st) suspend (default: 0 ms) +predelay: 0 + +# Post Resume Delay +# Include an N ms delay after (last) resume (default: 0 ms) +postdelay: 0 + +# Minimum Device Length +# graph only devices longer than min in the timeline (default: 0.001 ms) +mindev: 0.001 + +# ---- Debug Options ---- + +# Callgraph +# gather detailed ftrace callgraph data on all timeline events (default: false) +callgraph: false + +# Expand Callgraph +# pre-expand the callgraph data in the html output (default: disabled) +expandcg: false + +# Minimum Callgraph Length +# provide callgraph data for blocks longer than min (default: 0.001 ms) +mincg: 1 + +# Timestamp Precision +# Number of significant digits in timestamps (0:S, [3:ms], 6:us) +timeprec: 3 + +# Device Filter +# show only devs whose name/driver includes one of these strings +# devicefilter: _cpu_up,_cpu_down,i915,usb diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8 index fbe7bd3eae8e..18baaf6300c9 100644 --- a/tools/power/pm-graph/sleepgraph.8 +++ b/tools/power/pm-graph/sleepgraph.8 @@ -52,9 +52,32 @@ disable rtcwake and require a user keypress to resume. \fB-addlogs\fR Add the dmesg and ftrace logs to the html output. They will be viewable by clicking buttons in the timeline. +.TP +\fB-result \fIfile\fR +Export a results table to a text file for parsing. +.TP +\fB-sync\fR +Sync the filesystems before starting the test. This reduces the size of +the sys_sync call which happens in the suspend_prepare phase. +.TP +\fB-rs \fIenable/disable\fR +During test, enable/disable runtime suspend for all devices. The test is delayed +by 5 seconds to allow runtime suspend changes to occur. The settings are restored +after the test is complete. +.TP +\fB-display \fIon/off\fR +Turn the display on or off for the test using the xset command. This helps +maintain the consistecy of test data for better comparison. +.TP +\fB-skiphtml\fR +Run the test and capture the trace logs, but skip the timeline generation. .SS "advanced" .TP +\fB-gzip\fR +Gzip the trace and dmesg logs to save space. The tool can also read in gzipped +logs for processing. +.TP \fB-cmd \fIstr\fR Run the timeline over a custom suspend command, e.g. pm-suspend. By default the tool forces suspend via /sys/power/state so this allows testing over @@ -114,6 +137,18 @@ This reduces the html file size as there can be many tiny callgraphs which are barely visible in the timeline. The value is a float: e.g. 0.001 represents 1 us. .TP +\fB-cgfilter \fI"func1,func2,..."\fR +Reduce callgraph output in the timeline by limiting it to a list of calls. The +argument can be a single function name or a comma delimited list. +(default: none) +.TP +\fB-cgskip \fIfile\fR +Reduce callgraph timeline size by skipping over uninteresting functions +in the trace, e.g. printk or console_unlock. The functions listed +in this file will show up as empty leaves in the callgraph with only the start/end +times displayed. cgskip.txt is used automatically if found in the path, so +use "off" to disable completely (default: cgskip.txt) +.TP \fB-cgphase \fIp\fR Only show callgraph data for phase \fIp\fR (e.g. suspend_late). .TP @@ -122,6 +157,9 @@ In an x2 run, only show callgraph data for test \fIn\fR (e.g. 0 or 1). .TP \fB-timeprec \fIn\fR Number of significant digits in timestamps (0:S, [3:ms], 6:us). +.TP +\fB-bufsize \fIN\fR +Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB) .SH COMMANDS .TP @@ -144,11 +182,8 @@ Print out the contents of the ACPI Firmware Performance Data Table. \fB-sysinfo\fR Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. .TP -\fB-usbtopo\fR -Print out the current USB topology with power info. -.TP -\fB-usbauto\fR -Enable autosuspend for all connected USB devices. +\fB-devinfo\fR +Print out the pm settings of all devices which support runtime suspend. .TP \fB-flist\fR Print the list of ftrace functions currently being captured. Functions @@ -198,7 +233,7 @@ Execute a mem suspend with a 15 second wakeup. Include the logs in the html. .PP Execute a standby with a 15 second wakeup. Change the output folder name. .IP -\f(CW$ sudo sleepgraph -m standby -rtcwake 15 -o "standby-{hostname}-{date}-{time}"\fR +\f(CW$ sudo sleepgraph -m standby -rtcwake 15 -o "standby-{host}-{date}-{time}"\fR .PP Execute a freeze with no wakeup (require keypress). Change output folder name. .IP diff --git a/tools/power/pm-graph/analyze_suspend.py b/tools/power/pm-graph/sleepgraph.py index 1b60fe203741..266409fb27ae 100755 --- a/tools/power/pm-graph/analyze_suspend.py +++ b/tools/power/pm-graph/sleepgraph.py @@ -19,7 +19,7 @@ # Home Page # https://01.org/suspendresume # Source repo -# https://github.com/01org/pm-graph +# git@github.com:01org/pm-graph # # Description: # This tool is designed to assist kernel and OS developers in optimizing @@ -57,6 +57,7 @@ import platform from datetime import datetime import struct import ConfigParser +import gzip from threading import Thread from subprocess import call, Popen, PIPE @@ -68,8 +69,12 @@ from subprocess import call, Popen, PIPE # store system values and test parameters class SystemValues: title = 'SleepGraph' - version = '4.7' + version = '5.0' ansi = False + rs = 0 + display = 0 + gzip = False + sync = False verbose = False testlog = True dmesglog = False @@ -78,14 +83,19 @@ class SystemValues: mincglen = 0.0 cgphase = '' cgtest = -1 + cgskip = '' + multitest = {'run': False, 'count': 0, 'delay': 0} max_graph_depth = 0 callloopmaxgap = 0.0001 callloopmaxlen = 0.005 + bufsize = 0 cpucount = 0 memtotal = 204800 + memfree = 204800 srgap = 0 cgexp = False testdir = '' + outdir = '' tpath = '/sys/kernel/debug/tracing/' fpdtpath = '/sys/firmware/acpi/tables/FPDT' epath = '/sys/kernel/debug/tracing/events/power/' @@ -109,22 +119,24 @@ class SystemValues: dmesgfile = '' ftracefile = '' htmlfile = 'output.html' - embedded = False + result = '' rtcwake = True rtcwaketime = 15 rtcpath = '' devicefilter = [] + cgfilter = [] stamp = 0 execcount = 1 x2delay = 0 + skiphtml = False usecallgraph = False usetraceevents = False - usetraceeventsonly = False usetracemarkers = True usekprobes = True usedevsrc = False useprocmon = False notestrun = False + cgdump = False mixedphaseheight = True devprops = dict() predelay = 0 @@ -134,24 +146,33 @@ class SystemValues: tracertypefmt = '# tracer: (?P<t>.*)' firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$' tracefuncs = { - 'sys_sync': dict(), - 'pm_prepare_console': dict(), - 'pm_notifier_call_chain': dict(), - 'freeze_processes': dict(), - 'freeze_kernel_threads': dict(), - 'pm_restrict_gfp_mask': dict(), - 'acpi_suspend_begin': dict(), - 'suspend_console': dict(), - 'acpi_pm_prepare': dict(), - 'syscore_suspend': dict(), - 'arch_enable_nonboot_cpus_end': dict(), - 'syscore_resume': dict(), - 'acpi_pm_finish': dict(), - 'resume_console': dict(), - 'acpi_pm_end': dict(), - 'pm_restore_gfp_mask': dict(), - 'thaw_processes': dict(), - 'pm_restore_console': dict(), + 'sys_sync': {}, + '__pm_notifier_call_chain': {}, + 'pm_prepare_console': {}, + 'pm_notifier_call_chain': {}, + 'freeze_processes': {}, + 'freeze_kernel_threads': {}, + 'pm_restrict_gfp_mask': {}, + 'acpi_suspend_begin': {}, + 'acpi_hibernation_begin': {}, + 'acpi_hibernation_enter': {}, + 'acpi_hibernation_leave': {}, + 'acpi_pm_freeze': {}, + 'acpi_pm_thaw': {}, + 'hibernate_preallocate_memory': {}, + 'create_basic_memory_bitmaps': {}, + 'swsusp_write': {}, + 'suspend_console': {}, + 'acpi_pm_prepare': {}, + 'syscore_suspend': {}, + 'arch_enable_nonboot_cpus_end': {}, + 'syscore_resume': {}, + 'acpi_pm_finish': {}, + 'resume_console': {}, + 'acpi_pm_end': {}, + 'pm_restore_gfp_mask': {}, + 'thaw_processes': {}, + 'pm_restore_console': {}, 'CPU_OFF': { 'func':'_cpu_down', 'args_x86_64': {'cpu':'%di:s32'}, @@ -173,56 +194,54 @@ class SystemValues: 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 }, 'acpi_os_stall': {'ub': 1}, # ACPI - 'acpi_resume_power_resources': dict(), - 'acpi_ps_parse_aml': dict(), + 'acpi_resume_power_resources': {}, + 'acpi_ps_parse_aml': {}, # filesystem - 'ext4_sync_fs': dict(), + 'ext4_sync_fs': {}, # 80211 - 'iwlagn_mac_start': dict(), - 'iwlagn_alloc_bcast_station': dict(), - 'iwl_trans_pcie_start_hw': dict(), - 'iwl_trans_pcie_start_fw': dict(), - 'iwl_run_init_ucode': dict(), - 'iwl_load_ucode_wait_alive': dict(), - 'iwl_alive_start': dict(), - 'iwlagn_mac_stop': dict(), - 'iwlagn_mac_suspend': dict(), - 'iwlagn_mac_resume': dict(), - 'iwlagn_mac_add_interface': dict(), - 'iwlagn_mac_remove_interface': dict(), - 'iwlagn_mac_change_interface': dict(), - 'iwlagn_mac_config': dict(), - 'iwlagn_configure_filter': dict(), - 'iwlagn_mac_hw_scan': dict(), - 'iwlagn_bss_info_changed': dict(), - 'iwlagn_mac_channel_switch': dict(), - 'iwlagn_mac_flush': dict(), + 'iwlagn_mac_start': {}, + 'iwlagn_alloc_bcast_station': {}, + 'iwl_trans_pcie_start_hw': {}, + 'iwl_trans_pcie_start_fw': {}, + 'iwl_run_init_ucode': {}, + 'iwl_load_ucode_wait_alive': {}, + 'iwl_alive_start': {}, + 'iwlagn_mac_stop': {}, + 'iwlagn_mac_suspend': {}, + 'iwlagn_mac_resume': {}, + 'iwlagn_mac_add_interface': {}, + 'iwlagn_mac_remove_interface': {}, + 'iwlagn_mac_change_interface': {}, + 'iwlagn_mac_config': {}, + 'iwlagn_configure_filter': {}, + 'iwlagn_mac_hw_scan': {}, + 'iwlagn_bss_info_changed': {}, + 'iwlagn_mac_channel_switch': {}, + 'iwlagn_mac_flush': {}, # ATA 'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} }, # i915 - 'i915_gem_resume': dict(), - 'i915_restore_state': dict(), - 'intel_opregion_setup': dict(), - 'g4x_pre_enable_dp': dict(), - 'vlv_pre_enable_dp': dict(), - 'chv_pre_enable_dp': dict(), - 'g4x_enable_dp': dict(), - 'vlv_enable_dp': dict(), - 'intel_hpd_init': dict(), - 'intel_opregion_register': dict(), - 'intel_dp_detect': dict(), - 'intel_hdmi_detect': dict(), - 'intel_opregion_init': dict(), - 'intel_fbdev_set_suspend': dict(), + 'i915_gem_resume': {}, + 'i915_restore_state': {}, + 'intel_opregion_setup': {}, + 'g4x_pre_enable_dp': {}, + 'vlv_pre_enable_dp': {}, + 'chv_pre_enable_dp': {}, + 'g4x_enable_dp': {}, + 'vlv_enable_dp': {}, + 'intel_hpd_init': {}, + 'intel_opregion_register': {}, + 'intel_dp_detect': {}, + 'intel_hdmi_detect': {}, + 'intel_opregion_init': {}, + 'intel_fbdev_set_suspend': {}, } + cgblacklist = [] kprobes = dict() timeformat = '%.3f' + cmdline = '%s %s' % \ + (os.path.basename(sys.argv[0]), string.join(sys.argv[1:], ' ')) def __init__(self): - # if this is a phoronix test run, set some default options - if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): - self.embedded = True - self.dmesglog = self.ftracelog = True - self.htmlfile = os.environ['LOG_FILE'] self.archargs = 'args_'+platform.machine() self.hostname = platform.node() if(self.hostname == ''): @@ -237,18 +256,36 @@ class SystemValues: if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): self.ansi = True self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S') + def vprint(self, msg): + self.logmsg += msg+'\n' + if(self.verbose): + print(msg) def rootCheck(self, fatal=True): if(os.access(self.powerfile, os.W_OK)): return True if fatal: - doError('This command requires sysfs mount and root access') + msg = 'This command requires sysfs mount and root access' + print('ERROR: %s\n') % msg + self.outputResult({'error':msg}) + sys.exit() return False def rootUser(self, fatal=False): if 'USER' in os.environ and os.environ['USER'] == 'root': return True if fatal: - doError('This command must be run as root') + msg = 'This command must be run as root' + print('ERROR: %s\n') % msg + self.outputResult({'error':msg}) + sys.exit() return False + def getExec(self, cmd): + dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', + '/usr/local/sbin', '/usr/local/bin'] + for path in dirlist: + cmdfull = os.path.join(path, cmd) + if os.path.exists(cmdfull): + return cmdfull + return '' def setPrecision(self, num): if num < 0 or num > 6: return @@ -258,15 +295,15 @@ class SystemValues: n = datetime.now() args['date'] = n.strftime('%y%m%d') args['time'] = n.strftime('%H%M%S') - args['hostname'] = self.hostname + args['hostname'] = args['host'] = self.hostname return value.format(**args) def setOutputFile(self): if self.dmesgfile != '': - m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile) + m = re.match('(?P<name>.*)_dmesg\.txt.*', self.dmesgfile) if(m): self.htmlfile = m.group('name')+'.html' if self.ftracefile != '': - m = re.match('(?P<name>.*)_ftrace\.txt$', self.ftracefile) + m = re.match('(?P<name>.*)_ftrace\.txt.*', self.ftracefile) if(m): self.htmlfile = m.group('name')+'.html' def systemInfo(self, info): @@ -283,16 +320,19 @@ class SystemValues: c = info['processor-version'] if 'bios-version' in info: b = info['bios-version'] - self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d' % \ - (m, p, c, b, self.cpucount, self.memtotal) - def printSystemInfo(self): + self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \ + (m, p, c, b, self.cpucount, self.memtotal, self.memfree) + def printSystemInfo(self, fatal=False): self.rootCheck(True) - out = dmidecode(self.mempath, True) + out = dmidecode(self.mempath, fatal) + if len(out) < 1: + return fmt = '%-24s: %s' for name in sorted(out): print fmt % (name, out[name]) print fmt % ('cpucount', ('%d' % self.cpucount)) print fmt % ('memtotal', ('%d kB' % self.memtotal)) + print fmt % ('memfree', ('%d kB' % self.memfree)) def cpuInfo(self): self.cpucount = 0 fp = open('/proc/cpuinfo', 'r') @@ -305,7 +345,9 @@ class SystemValues: m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line) if m: self.memtotal = int(m.group('sz')) - break + m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line) + if m: + self.memfree = int(m.group('sz')) fp.close() def initTestOutput(self, name): self.prefix = self.hostname @@ -315,39 +357,34 @@ class SystemValues: testtime = datetime.now().strftime(fmt) self.teststamp = \ '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver - if(self.embedded): - self.dmesgfile = \ - '/tmp/'+testtime+'_'+self.suspendmode+'_dmesg.txt' - self.ftracefile = \ - '/tmp/'+testtime+'_'+self.suspendmode+'_ftrace.txt' - return + ext = '' + if self.gzip: + ext = '.gz' self.dmesgfile = \ - self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt' + self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'+ext self.ftracefile = \ - self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt' + self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'+ext self.htmlfile = \ self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' if not os.path.isdir(self.testdir): os.mkdir(self.testdir) + def getValueList(self, value): + out = [] + for i in value.split(','): + if i.strip(): + out.append(i.strip()) + return out def setDeviceFilter(self, value): - self.devicefilter = [] - if value: - value = value.split(',') - for i in value: - self.devicefilter.append(i.strip()) + self.devicefilter = self.getValueList(value) + def setCallgraphFilter(self, value): + self.cgfilter = self.getValueList(value) + def setCallgraphBlacklist(self, file): + self.cgblacklist = self.listFromFile(file) def rtcWakeAlarmOn(self): call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True) - outD = open(self.rtcpath+'/date', 'r').read().strip() - outT = open(self.rtcpath+'/time', 'r').read().strip() - mD = re.match('^(?P<y>[0-9]*)-(?P<m>[0-9]*)-(?P<d>[0-9]*)', outD) - mT = re.match('^(?P<h>[0-9]*):(?P<m>[0-9]*):(?P<s>[0-9]*)', outT) - if(mD and mT): - # get the current time from hardware - utcoffset = int((datetime.now() - datetime.utcnow()).total_seconds()) - dt = datetime(\ - int(mD.group('y')), int(mD.group('m')), int(mD.group('d')), - int(mT.group('h')), int(mT.group('m')), int(mT.group('s'))) - nowtime = int(dt.strftime('%s')) + utcoffset + nowtime = open(self.rtcpath+'/since_epoch', 'r').read().strip() + if nowtime: + nowtime = int(nowtime) else: # if hardware time fails, use the software time nowtime = int(datetime.now().strftime('%s')) @@ -369,10 +406,10 @@ class SystemValues: ktime = m.group('ktime') fp.close() self.dmesgstart = float(ktime) - def getdmesg(self): + def getdmesg(self, fwdata=[]): + op = self.writeDatafileHeader(sysvals.dmesgfile, fwdata) # store all new dmesg lines since initdmesg was called fp = Popen('dmesg', stdout=PIPE).stdout - op = open(self.dmesgfile, 'a') for line in fp: line = line.replace('\r\n', '') idx = line.find('[') @@ -386,11 +423,17 @@ class SystemValues: op.write(line) fp.close() op.close() - def addFtraceFilterFunctions(self, file): + def listFromFile(self, file): + list = [] fp = open(file) - list = fp.read().split('\n') + for i in fp.read().split('\n'): + i = i.strip() + if i and i[0] != '#': + list.append(i) fp.close() - for i in list: + return list + def addFtraceFilterFunctions(self, file): + for i in self.listFromFile(file): if len(i) < 2: continue self.tracefuncs[i] = dict() @@ -399,9 +442,7 @@ class SystemValues: if not current: call('cat '+self.tpath+'available_filter_functions', shell=True) return - fp = open(self.tpath+'available_filter_functions') - master = fp.read().split('\n') - fp.close() + master = self.listFromFile(self.tpath+'available_filter_functions') for i in self.tracefuncs: if 'func' in self.tracefuncs[i]: i = self.tracefuncs[i]['func'] @@ -410,9 +451,7 @@ class SystemValues: else: print self.colorText(i) def setFtraceFilterFunctions(self, list): - fp = open(self.tpath+'available_filter_functions') - master = fp.read().split('\n') - fp.close() + master = self.listFromFile(self.tpath+'available_filter_functions') flist = '' for i in list: if i not in master: @@ -501,6 +540,7 @@ class SystemValues: rejects = [] # sort kprobes: trace, ub-dev, custom, dev kpl = [[], [], [], []] + linesout = len(self.kprobes) for name in sorted(self.kprobes): res = self.colorText('YES', 32) if not self.testKprobe(name, self.kprobes[name]): @@ -528,17 +568,10 @@ class SystemValues: for kp in kplist: kprobeevents += self.kprobeText(kp, self.kprobes[kp]) self.fsetVal(kprobeevents, 'kprobe_events') - # verify that the kprobes were set as ordered - check = self.fgetVal('kprobe_events') - linesout = len(kprobeevents.split('\n')) - 1 - linesack = len(check.split('\n')) - 1 if output: - res = '%d/%d' % (linesack, linesout) - if linesack < linesout: - res = self.colorText(res, 31) - else: - res = self.colorText(res, 32) - print(' working kprobe functions enabled: %s' % res) + check = self.fgetVal('kprobe_events') + linesack = (len(check.split('\n')) - 1) / 2 + print(' kprobe functions enabled: %d/%d' % (linesack, linesout)) self.fsetVal('1', 'events/kprobes/enable') def testKprobe(self, kname, kprobe): self.fsetVal('0', 'events/kprobes/enable') @@ -555,8 +588,7 @@ class SystemValues: if linesack < linesout: return False return True - def fsetVal(self, val, path, mode='w'): - file = self.tpath+path + def setVal(self, val, file, mode='w'): if not os.path.exists(file): return False try: @@ -567,8 +599,9 @@ class SystemValues: except: return False return True - def fgetVal(self, path): - file = self.tpath+path + def fsetVal(self, val, path, mode='w'): + return self.setVal(val, self.tpath+path, mode) + def getVal(self, file): res = '' if not os.path.exists(file): return res @@ -579,10 +612,13 @@ class SystemValues: except: pass return res + def fgetVal(self, path): + return self.getVal(self.tpath+path) def cleanupFtrace(self): - if(self.usecallgraph or self.usetraceevents): + if(self.usecallgraph or self.usetraceevents or self.usedevsrc): self.fsetVal('0', 'events/kprobes/enable') self.fsetVal('', 'kprobe_events') + self.fsetVal('1024', 'buffer_size_kb') def setupAllKprobes(self): for name in self.tracefuncs: self.defaultKprobe(name, self.tracefuncs[name]) @@ -599,7 +635,8 @@ class SystemValues: if name == f: return True return False - def initFtrace(self, testing=False): + def initFtrace(self): + self.printSystemInfo(False) print('INITIALIZING FTRACE...') # turn trace off self.fsetVal('0', 'tracing_on') @@ -607,17 +644,21 @@ class SystemValues: # set the trace clock to global self.fsetVal('global', 'trace_clock') self.fsetVal('nop', 'current_tracer') - # set trace buffer to a huge value - if self.usecallgraph or self.usedevsrc: - tgtsize = min(self.memtotal / 2, 2*1024*1024) - maxbuf = '%d' % (tgtsize / max(1, self.cpucount)) - if self.cpucount < 1 or not self.fsetVal(maxbuf, 'buffer_size_kb'): - self.fsetVal('131072', 'buffer_size_kb') + # set trace buffer to an appropriate value + cpus = max(1, self.cpucount) + if self.bufsize > 0: + tgtsize = self.bufsize + elif self.usecallgraph or self.usedevsrc: + tgtsize = min(self.memfree, 3*1024*1024) else: - self.fsetVal('16384', 'buffer_size_kb') - # go no further if this is just a status check - if testing: - return + tgtsize = 65536 + while not self.fsetVal('%d' % (tgtsize / cpus), 'buffer_size_kb'): + # if the size failed to set, lower it and keep trying + tgtsize -= 65536 + if tgtsize < 65536: + tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus + break + print 'Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus) # initialize the callgraph trace if(self.usecallgraph): # set trace type @@ -635,7 +676,7 @@ class SystemValues: self.fsetVal('graph-time', 'trace_options') self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth') cf = ['dpm_run_callback'] - if(self.usetraceeventsonly): + if(self.usetraceevents): cf += ['dpm_prepare', 'dpm_complete'] for fn in self.tracefuncs: if 'func' in self.tracefuncs[fn]: @@ -688,16 +729,65 @@ class SystemValues: return str return '\x1B[%d;40m%s\x1B[m' % (color, str) def writeDatafileHeader(self, filename, fwdata=[]): - fp = open(filename, 'w') - fp.write(self.teststamp+'\n') - fp.write(self.sysstamp+'\n') + fp = self.openlog(filename, 'w') + fp.write('%s\n%s\n# command | %s\n' % (self.teststamp, self.sysstamp, self.cmdline)) if(self.suspendmode == 'mem' or self.suspendmode == 'command'): for fw in fwdata: if(fw): fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) + return fp + def sudouser(self, dir): + if os.path.exists(dir) and os.getuid() == 0 and \ + 'SUDO_USER' in os.environ: + cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' + call(cmd.format(os.environ['SUDO_USER'], dir), shell=True) + def outputResult(self, testdata, num=0): + if not self.result: + return + n = '' + if num > 0: + n = '%d' % num + fp = open(self.result, 'a') + if 'error' in testdata: + fp.write('result%s: fail\n' % n) + fp.write('error%s: %s\n' % (n, testdata['error'])) + else: + fp.write('result%s: pass\n' % n) + for v in ['suspend', 'resume', 'boot', 'lastinit']: + if v in testdata: + fp.write('%s%s: %.3f\n' % (v, n, testdata[v])) + for v in ['fwsuspend', 'fwresume']: + if v in testdata: + fp.write('%s%s: %.3f\n' % (v, n, testdata[v] / 1000000.0)) + if 'bugurl' in testdata: + fp.write('url%s: %s\n' % (n, testdata['bugurl'])) fp.close() + self.sudouser(self.result) + def configFile(self, file): + dir = os.path.dirname(os.path.realpath(__file__)) + if os.path.exists(file): + return file + elif os.path.exists(dir+'/'+file): + return dir+'/'+file + elif os.path.exists(dir+'/config/'+file): + return dir+'/config/'+file + return '' + def openlog(self, filename, mode): + isgz = self.gzip + if mode == 'r': + try: + with gzip.open(filename, mode+'b') as fp: + test = fp.read(64) + isgz = True + except: + isgz = False + if isgz: + return gzip.open(filename, mode+'b') + return open(filename, mode) sysvals = SystemValues() +switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] +switchoff = ['disable', 'off', 'false', '0'] suspendmodename = { 'freeze': 'Freeze (S0)', 'standby': 'Standby (S1)', @@ -826,34 +916,65 @@ class Data: for phase in self.phases: self.devicegroups.append([phase]) self.errorinfo = {'suspend':[],'resume':[]} - def extractErrorInfo(self, dmesg): - error = '' - tm = 0.0 - for i in range(len(dmesg)): - if 'Call Trace:' in dmesg[i]: - m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) .*', dmesg[i]) - if not m: - continue - tm = float(m.group('ktime')) - if tm < self.start or tm > self.end: - continue - for j in range(i-10, i+1): - error += dmesg[j] + def extractErrorInfo(self): + lf = sysvals.openlog(sysvals.dmesgfile, 'r') + i = 0 + list = [] + # sl = start line, et = error time, el = error line + type = 'ERROR' + sl = et = el = -1 + for line in lf: + i += 1 + m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) + if not m: continue - if error: - m = re.match('[ \t]*\[ *[0-9\.]*\] \[\<[0-9a-fA-F]*\>\] .*', dmesg[i]) - if m: - error += dmesg[i] - else: - if tm < self.tSuspended: - dir = 'suspend' - else: - dir = 'resume' - error = error.replace('<', '<').replace('>', '>') - vprint('kernel error found in %s at %f' % (dir, tm)) - self.errorinfo[dir].append((tm, error)) + t = float(m.group('ktime')) + if t < self.start or t > self.end: + continue + if t < self.tSuspended: + dir = 'suspend' + else: + dir = 'resume' + msg = m.group('msg') + if re.match('-*\[ *cut here *\]-*', msg): + type = 'WARNING' + sl = i + elif re.match('genirq: .*', msg): + type = 'IRQ' + sl = i + elif re.match('BUG: .*', msg) or re.match('kernel BUG .*', msg): + type = 'BUG' + sl = i + elif re.match('-*\[ *end trace .*\]-*', msg) or \ + re.match('R13: .*', msg): + if et >= 0 and sl >= 0: + list.append((type, dir, et, sl, i)) self.kerror = True - error = '' + sl = et = el = -1 + type = 'ERROR' + elif 'Call Trace:' in msg: + if el >= 0 and et >= 0: + list.append((type, dir, et, el, el)) + self.kerror = True + et, el = t, i + if sl < 0 or type == 'BUG': + slval = i + if sl >= 0: + slval = sl + list.append((type, dir, et, slval, i)) + self.kerror = True + sl = et = el = -1 + type = 'ERROR' + if el >= 0 and et >= 0: + list.append((type, dir, et, el, el)) + self.kerror = True + for e in list: + type, dir, t, idx1, idx2 = e + sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) + self.errorinfo[dir].append((type, t, idx1, idx2)) + if self.kerror: + sysvals.dmesglog = True + lf.close() def setStart(self, time): self.start = time def setEnd(self, time): @@ -867,6 +988,14 @@ class Data: time < d['end']): return False return True + def phaseCollision(self, phase, isbegin, line): + key = 'end' + if isbegin: + key = 'start' + if self.dmesg[phase][key] >= 0: + sysvals.vprint('IGNORE: %s' % line.strip()) + return True + return False def sourcePhase(self, start): for phase in self.phases: pend = self.dmesg[phase]['end'] @@ -918,7 +1047,7 @@ class Data: return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata) # this should not happen if not tgtdev: - vprint('[%f - %f] %s-%d %s %s %s' % \ + sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \ (start, end, proc, pid, kprobename, cdata, rdata)) return False # place the call data inside the src element of the tgtdev @@ -1054,6 +1183,13 @@ class Data: if('src' in d): for e in d['src']: e.time = self.trimTimeVal(e.time, t0, dT, left) + for dir in ['suspend', 'resume']: + list = [] + for e in self.errorinfo[dir]: + type, tm, idx1, idx2 = e + tm = self.trimTimeVal(tm, t0, dT, left) + list.append((type, tm, idx1, idx2)) + self.errorinfo[dir] = list def normalizeTime(self, tZero): # trim out any standby or freeze clock time if(self.tSuspended != self.tResumed): @@ -1100,7 +1236,7 @@ class Data: if self.dmesg[p]['end'] > dev['start']: dev['end'] = self.dmesg[p]['end'] break - vprint('%s (%s): callback didnt return' % (devname, phase)) + sysvals.vprint('%s (%s): callback didnt return' % (devname, phase)) def deviceFilter(self, devicefilter): for phase in self.phases: list = self.dmesg[phase]['list'] @@ -1200,15 +1336,15 @@ class Data: devlist.append(child) return devlist def printDetails(self): - vprint('Timeline Details:') - vprint(' test start: %f' % self.start) - vprint('kernel suspend start: %f' % self.tKernSus) + sysvals.vprint('Timeline Details:') + sysvals.vprint(' test start: %f' % self.start) + sysvals.vprint('kernel suspend start: %f' % self.tKernSus) for phase in self.phases: dc = len(self.dmesg[phase]['list']) - vprint(' %16s: %f - %f (%d devices)' % (phase, \ + sysvals.vprint(' %16s: %f - %f (%d devices)' % (phase, \ self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc)) - vprint(' kernel resume end: %f' % self.tKernRes) - vprint(' test end: %f' % self.end) + sysvals.vprint(' kernel resume end: %f' % self.tKernRes) + sysvals.vprint(' test end: %f' % self.end) def deviceChildrenAllPhases(self, devname): devlist = [] for phase in self.phases: @@ -1358,14 +1494,21 @@ class Data: tres.append(t) # process the events for suspend and resume if len(proclist) > 0: - vprint('Process Execution:') + sysvals.vprint('Process Execution:') for ps in proclist: c = self.addProcessUsageEvent(ps, tsus) if c > 0: - vprint('%25s (sus): %d' % (ps, c)) + sysvals.vprint('%25s (sus): %d' % (ps, c)) c = self.addProcessUsageEvent(ps, tres) if c > 0: - vprint('%25s (res): %d' % (ps, c)) + sysvals.vprint('%25s (res): %d' % (ps, c)) + def debugPrint(self): + for p in self.phases: + list = self.dmesg[p]['list'] + for devname in list: + dev = list[devname] + if 'ftrace' in dev: + dev['ftrace'].debugPrint(' [%s]' % devname) # Class: DevFunction # Description: @@ -1504,18 +1647,24 @@ class FTraceLine: # something else (possibly a trace marker) else: self.name = m + def isCall(self): + return self.fcall and not self.freturn + def isReturn(self): + return self.freturn and not self.fcall + def isLeaf(self): + return self.fcall and self.freturn def getDepth(self, str): return len(str)/2 - def debugPrint(self, dev=''): - if(self.freturn and self.fcall): - print('%s -- %f (%02d): %s(); (%.3f us)' % (dev, self.time, \ - self.depth, self.name, self.length*1000000)) - elif(self.freturn): - print('%s -- %f (%02d): %s} (%.3f us)' % (dev, self.time, \ - self.depth, self.name, self.length*1000000)) + def debugPrint(self, info=''): + if self.isLeaf(): + print(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \ + self.depth, self.name, self.length*1000000, info)) + elif self.freturn: + print(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \ + self.depth, self.name, self.length*1000000, info)) else: - print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \ - self.depth, self.name, self.length*1000000)) + print(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \ + self.depth, self.name, self.length*1000000, info)) def startMarker(self): # Is this the starting line of a suspend? if not self.fevent: @@ -1558,107 +1707,160 @@ class FTraceCallGraph: depth = 0 pid = 0 name = '' - def __init__(self, pid): + partial = False + vfname = 'missing_function_name' + ignore = False + sv = 0 + def __init__(self, pid, sv): self.start = -1.0 self.end = -1.0 self.list = [] self.depth = 0 self.pid = pid - def addLine(self, line, debug=False): + self.sv = sv + def addLine(self, line): # if this is already invalid, just leave if(self.invalid): - return False - # invalidate on too much data or bad depth - if(len(self.list) >= 1000000 or self.depth < 0): + if(line.depth == 0 and line.freturn): + return 1 + return 0 + # invalidate on bad depth + if(self.depth < 0): self.invalidate(line) - return False + return 0 + # ignore data til we return to the current depth + if self.ignore: + if line.depth > self.depth: + return 0 + else: + self.list[-1].freturn = True + self.list[-1].length = line.time - self.list[-1].time + self.ignore = False + # if this is a return at self.depth, no more work is needed + if line.depth == self.depth and line.isReturn(): + if line.depth == 0: + self.end = line.time + return 1 + return 0 # compare current depth with this lines pre-call depth prelinedep = line.depth - if(line.freturn and not line.fcall): + if line.isReturn(): prelinedep += 1 last = 0 lasttime = line.time - virtualfname = 'missing_function_name' if len(self.list) > 0: last = self.list[-1] lasttime = last.time + if last.isLeaf(): + lasttime += last.length # handle low misalignments by inserting returns - if prelinedep < self.depth: - if debug and last: - print '-------- task %d --------' % self.pid - last.debugPrint() + mismatch = prelinedep - self.depth + warning = self.sv.verbose and abs(mismatch) > 1 + info = [] + if mismatch < 0: idx = 0 # add return calls to get the depth down while prelinedep < self.depth: - if debug: - print 'MISALIGN LOW (add returns): C%d - eC%d' % (self.depth, prelinedep) self.depth -= 1 - if idx == 0 and last and last.fcall and not last.freturn: + if idx == 0 and last and last.isCall(): # special case, turn last call into a leaf last.depth = self.depth last.freturn = True last.length = line.time - last.time - if debug: - last.debugPrint() + if warning: + info.append(('[make leaf]', last)) else: vline = FTraceLine(lasttime) vline.depth = self.depth - vline.name = virtualfname + vline.name = self.vfname vline.freturn = True self.list.append(vline) - if debug: - vline.debugPrint() + if warning: + if idx == 0: + info.append(('', last)) + info.append(('[add return]', vline)) idx += 1 - if debug: - line.debugPrint() - print '' + if warning: + info.append(('', line)) # handle high misalignments by inserting calls - elif prelinedep > self.depth: - if debug and last: - print '-------- task %d --------' % self.pid - last.debugPrint() + elif mismatch > 0: idx = 0 + if warning: + info.append(('', last)) # add calls to get the depth up while prelinedep > self.depth: - if debug: - print 'MISALIGN HIGH (add calls): C%d - eC%d' % (self.depth, prelinedep) - if idx == 0 and line.freturn and not line.fcall: + if idx == 0 and line.isReturn(): # special case, turn this return into a leaf line.fcall = True prelinedep -= 1 + if warning: + info.append(('[make leaf]', line)) else: vline = FTraceLine(lasttime) vline.depth = self.depth - vline.name = virtualfname + vline.name = self.vfname vline.fcall = True - if debug: - vline.debugPrint() self.list.append(vline) self.depth += 1 if not last: self.start = vline.time + if warning: + info.append(('[add call]', vline)) idx += 1 - if debug: - line.debugPrint() - print '' + if warning and ('[make leaf]', line) not in info: + info.append(('', line)) + if warning: + print 'WARNING: ftrace data missing, corrections made:' + for i in info: + t, obj = i + if obj: + obj.debugPrint(t) # process the call and set the new depth - if(line.fcall and not line.freturn): - self.depth += 1 - elif(line.freturn and not line.fcall): + skipadd = False + md = self.sv.max_graph_depth + if line.isCall(): + # ignore blacklisted/overdepth funcs + if (md and self.depth >= md - 1) or (line.name in self.sv.cgblacklist): + self.ignore = True + else: + self.depth += 1 + elif line.isReturn(): self.depth -= 1 + # remove blacklisted/overdepth/empty funcs that slipped through + if (last and last.isCall() and last.depth == line.depth) or \ + (md and last and last.depth >= md) or \ + (line.name in self.sv.cgblacklist): + while len(self.list) > 0 and self.list[-1].depth > line.depth: + self.list.pop(-1) + if len(self.list) == 0: + self.invalid = True + return 1 + self.list[-1].freturn = True + self.list[-1].length = line.time - self.list[-1].time + self.list[-1].name = line.name + skipadd = True if len(self.list) < 1: self.start = line.time - self.list.append(line) + # check for a mismatch that returned all the way to callgraph end + res = 1 + if mismatch < 0 and self.list[-1].depth == 0 and self.list[-1].freturn: + line = self.list[-1] + skipadd = True + res = -1 + if not skipadd: + self.list.append(line) if(line.depth == 0 and line.freturn): if(self.start < 0): self.start = line.time self.end = line.time if line.fcall: self.end += line.length - if self.list[0].name == virtualfname: + if self.list[0].name == self.vfname: self.invalid = True - return True - return False + if res == -1: + self.partial = True + return res + return 0 def invalidate(self, line): if(len(self.list) > 0): first = self.list[0] @@ -1668,29 +1870,30 @@ class FTraceCallGraph: id = 'task %s' % (self.pid) window = '(%f - %f)' % (self.start, line.time) if(self.depth < 0): - vprint('Too much data for '+id+\ + print('Data misalignment for '+id+\ ' (buffer overflow), ignoring this callback') else: - vprint('Too much data for '+id+\ + print('Too much data for '+id+\ ' '+window+', ignoring this callback') - def slice(self, t0, tN): - minicg = FTraceCallGraph(0) - count = -1 - firstdepth = 0 + def slice(self, dev): + minicg = FTraceCallGraph(dev['pid'], self.sv) + minicg.name = self.name + mydepth = -1 + good = False for l in self.list: - if(l.time < t0 or l.time > tN): + if(l.time < dev['start'] or l.time > dev['end']): continue - if(count < 0): - if(not l.fcall or l.name == 'dev_driver_string'): - continue - firstdepth = l.depth - count = 0 - l.depth -= firstdepth - minicg.addLine(l) - if((count == 0 and l.freturn and l.fcall) or - (count > 0 and l.depth <= 0)): + if mydepth < 0: + if l.name == 'mutex_lock' and l.freturn: + mydepth = l.depth + continue + elif l.depth == mydepth and l.name == 'mutex_unlock' and l.fcall: + good = True break - count += 1 + l.depth -= mydepth + minicg.addLine(l) + if not good or len(minicg.list) < 1: + return 0 return minicg def repair(self, enddepth): # bring the depth back to 0 with additional returns @@ -1701,11 +1904,11 @@ class FTraceCallGraph: t.depth = i t.freturn = True fixed = self.addLine(t) - if fixed: + if fixed != 0: self.end = last.time return True return False - def postProcess(self, debug=False): + def postProcess(self): if len(self.list) > 0: self.name = self.list[0].name stack = dict() @@ -1714,20 +1917,23 @@ class FTraceCallGraph: for l in self.list: # ftrace bug: reported duration is not reliable # check each leaf and clip it at max possible length - if(last and last.freturn and last.fcall): + if last and last.isLeaf(): if last.length > l.time - last.time: last.length = l.time - last.time - if(l.fcall and not l.freturn): + if l.isCall(): stack[l.depth] = l cnt += 1 - elif(l.freturn and not l.fcall): + elif l.isReturn(): if(l.depth not in stack): - if debug: + if self.sv.verbose: print 'Post Process Error: Depth missing' l.debugPrint() return False # calculate call length from call/return lines - stack[l.depth].length = l.time - stack[l.depth].time + cl = stack[l.depth] + cl.length = l.time - cl.time + if cl.name == self.vfname: + cl.name = l.name stack.pop(l.depth) l.length = 0 cnt -= 1 @@ -1736,13 +1942,13 @@ class FTraceCallGraph: # trace caught the whole call tree return True elif(cnt < 0): - if debug: + if self.sv.verbose: print 'Post Process Error: Depth is less than 0' return False # trace ended before call tree finished return self.repair(cnt) def deviceMatch(self, pid, data): - found = False + found = '' # add the callgraph data to the device hierarchy borderphase = { 'dpm_prepare': 'suspend_prepare', @@ -1756,8 +1962,10 @@ class FTraceCallGraph: if(pid == dev['pid'] and self.start <= dev['start'] and self.end >= dev['end']): - dev['ftrace'] = self.slice(dev['start'], dev['end']) - found = True + cg = self.slice(dev) + if cg: + dev['ftrace'] = cg + found = devname return found for p in data.phases: if(data.dmesg[p]['start'] <= self.start and @@ -1769,7 +1977,7 @@ class FTraceCallGraph: self.start <= dev['start'] and self.end >= dev['end']): dev['ftrace'] = self - found = True + found = devname break break return found @@ -1793,18 +2001,20 @@ class FTraceCallGraph: if out: phase, myname = out data.dmesg[phase]['list'][myname]['ftrace'] = self - def debugPrint(self): - print('[%f - %f] %s (%d)') % (self.start, self.end, self.name, self.pid) + def debugPrint(self, info=''): + print('%s pid=%d [%f - %f] %.3f us') % \ + (self.name, self.pid, self.start, self.end, + (self.end - self.start)*1000000) for l in self.list: - if(l.freturn and l.fcall): - print('%f (%02d): %s(); (%.3f us)' % (l.time, \ - l.depth, l.name, l.length*1000000)) - elif(l.freturn): - print('%f (%02d): %s} (%.3f us)' % (l.time, \ - l.depth, l.name, l.length*1000000)) + if l.isLeaf(): + print('%f (%02d): %s(); (%.3f us)%s' % (l.time, \ + l.depth, l.name, l.length*1000000, info)) + elif l.freturn: + print('%f (%02d): %s} (%.3f us)%s' % (l.time, \ + l.depth, l.name, l.length*1000000, info)) else: - print('%f (%02d): %s() { (%.3f us)' % (l.time, \ - l.depth, l.name, l.length*1000000)) + print('%f (%02d): %s() { (%.3f us)%s' % (l.time, \ + l.depth, l.name, l.length*1000000, info)) print(' ') class DevItem: @@ -1839,8 +2049,8 @@ class Timeline: self.rowH = rowheight self.scaleH = scaleheight self.html = '' - def createHeader(self, sv): - if(not sv.stamp['time']): + def createHeader(self, sv, stamp): + if(not stamp['time']): return self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ % (sv.title, sv.version) @@ -1851,12 +2061,12 @@ class Timeline: if sv.ftracelog: self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>' headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' - self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'], - sv.stamp['mode'], sv.stamp['time']) - if 'man' in sv.stamp and 'plat' in sv.stamp and 'cpu' in sv.stamp: + self.html += headline_stamp.format(stamp['host'], stamp['kernel'], + stamp['mode'], stamp['time']) + if 'man' in stamp and 'plat' in stamp and 'cpu' in stamp and \ + stamp['man'] and stamp['plat'] and stamp['cpu']: headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n' - self.html += headline_sysinfo.format(sv.stamp['man'], - sv.stamp['plat'], sv.stamp['cpu']) + self.html += headline_sysinfo.format(stamp['man'], stamp['plat'], stamp['cpu']) # Function: getDeviceRows # Description: @@ -2067,12 +2277,16 @@ class Timeline: class TestProps: stamp = '' sysinfo = '' + cmdline = '' + kparams = '' S0i3 = False fwdata = [] stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' sysinfofmt = '^# sysinfo .*' + cmdlinefmt = '^# command \| (?P<cmd>.*)' + kparamsfmt = '^# kparams \| (?P<kp>.*)' ftrace_line_fmt_fg = \ '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\ ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\ @@ -2116,13 +2330,20 @@ class TestProps: sv.hostname = data.stamp['host'] sv.suspendmode = data.stamp['mode'] if sv.suspendmode == 'command' and sv.ftracefile != '': - modes = ['on', 'freeze', 'standby', 'mem'] - out = Popen(['grep', 'suspend_enter', sv.ftracefile], + modes = ['on', 'freeze', 'standby', 'mem', 'disk'] + out = Popen(['grep', 'machine_suspend', sv.ftracefile], stderr=PIPE, stdout=PIPE).stdout.read() - m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) - if m and m.group('mode') in ['1', '2', '3']: + m = re.match('.* machine_suspend\[(?P<mode>.*)\]', out) + if m and m.group('mode') in ['1', '2', '3', '4']: sv.suspendmode = modes[int(m.group('mode'))] data.stamp['mode'] = sv.suspendmode + m = re.match(self.cmdlinefmt, self.cmdline) + if m: + sv.cmdline = m.group('cmd') + if self.kparams: + m = re.match(self.kparamsfmt, self.kparams) + if m: + sv.kparams = m.group('kp') if not sv.stamp: sv.stamp = data.stamp @@ -2186,47 +2407,43 @@ class ProcessMonitor: # ----------------- FUNCTIONS -------------------- -# Function: vprint -# Description: -# verbose print (prints only with -verbose option) -# Arguments: -# msg: the debug/log message to print -def vprint(msg): - sysvals.logmsg += msg+'\n' - if(sysvals.verbose): - print(msg) - # Function: doesTraceLogHaveTraceEvents # Description: -# Quickly determine if the ftrace log has some or all of the trace events -# required for primary parsing. Set the usetraceevents and/or -# usetraceeventsonly flags in the global sysvals object +# Quickly determine if the ftrace log has all of the trace events, +# markers, and/or kprobes required for primary parsing. def doesTraceLogHaveTraceEvents(): - # check for kprobes + kpcheck = ['_cal: (', '_cpu_down()'] + techeck = sysvals.traceevents[:] + tmcheck = ['SUSPEND START', 'RESUME COMPLETE'] sysvals.usekprobes = False - out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True) - if(out == 0): - sysvals.usekprobes = True - # check for callgraph data on trace event blocks - out = call('grep -q "_cpu_down()" '+sysvals.ftracefile, shell=True) - if(out == 0): - sysvals.usekprobes = True - out = Popen(['head', '-1', sysvals.ftracefile], - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') - # figure out what level of trace events are supported - sysvals.usetraceeventsonly = True - sysvals.usetraceevents = False - for e in sysvals.traceevents: - out = call('grep -q "'+e+': " '+sysvals.ftracefile, shell=True) - if(out != 0): - sysvals.usetraceeventsonly = False - if(e == 'suspend_resume' and out == 0): - sysvals.usetraceevents = True - # determine is this log is properly formatted - for e in ['SUSPEND START', 'RESUME COMPLETE']: - out = call('grep -q "'+e+'" '+sysvals.ftracefile, shell=True) - if(out != 0): - sysvals.usetracemarkers = False + fp = sysvals.openlog(sysvals.ftracefile, 'r') + for line in fp: + # check for kprobes + if not sysvals.usekprobes: + for i in kpcheck: + if i in line: + sysvals.usekprobes = True + # check for all necessary trace events + check = techeck[:] + for i in techeck: + if i in line: + check.remove(i) + techeck = check + # check for all necessary trace markers + check = tmcheck[:] + for i in tmcheck: + if i in line: + check.remove(i) + tmcheck = check + fp.close() + if len(techeck) == 0: + sysvals.usetraceevents = True + else: + sysvals.usetraceevents = False + if len(tmcheck) == 0: + sysvals.usetracemarkers = True + else: + sysvals.usetracemarkers = False # Function: appendIncompleteTraceLog # Description: @@ -2247,9 +2464,10 @@ def appendIncompleteTraceLog(testruns): testrun.append(TestRun(data)) # extract the callgraph and traceevent data - vprint('Analyzing the ftrace data...') + sysvals.vprint('Analyzing the ftrace data (%s)...' % \ + os.path.basename(sysvals.ftracefile)) tp = TestProps() - tf = open(sysvals.ftracefile, 'r') + tf = sysvals.openlog(sysvals.ftracefile, 'r') data = 0 for line in tf: # remove any latent carriage returns @@ -2261,6 +2479,9 @@ def appendIncompleteTraceLog(testruns): elif re.match(tp.sysinfofmt, line): tp.sysinfo = line continue + elif re.match(tp.cmdlinefmt, line): + tp.cmdline = line + continue # determine the trace data type (required for further parsing) m = re.match(sysvals.tracertypefmt, line) if(m): @@ -2393,11 +2614,14 @@ def appendIncompleteTraceLog(testruns): # create a callgraph object for the data if(pid not in testrun[testidx].ftemp): testrun[testidx].ftemp[pid] = [] - testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid)) + testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals)) # when the call is finished, see which device matches it cg = testrun[testidx].ftemp[pid][-1] - if(cg.addLine(t)): - testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid)) + res = cg.addLine(t) + if(res != 0): + testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals)) + if(res == -1): + testrun[testidx].ftemp[pid][-1].addLine(t) tf.close() for test in testrun: @@ -2410,11 +2634,11 @@ def appendIncompleteTraceLog(testruns): # add the callgraph data to the device hierarchy for pid in test.ftemp: for cg in test.ftemp[pid]: - if len(cg.list) < 1 or cg.invalid: + if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0): continue if(not cg.postProcess()): id = 'task %s cpu %s' % (pid, m.group('cpu')) - vprint('Sanity check failed for '+\ + sysvals.vprint('Sanity check failed for '+\ id+', ignoring this callback') continue callstart = cg.start @@ -2431,8 +2655,6 @@ def appendIncompleteTraceLog(testruns): dev['ftrace'] = cg break - test.data.printDetails() - # Function: parseTraceLog # Description: # Analyze an ftrace log output file generated from this app during @@ -2441,12 +2663,13 @@ def appendIncompleteTraceLog(testruns): # The ftrace filename is taken from sysvals # Output: # An array of Data objects -def parseTraceLog(): - vprint('Analyzing the ftrace data...') +def parseTraceLog(live=False): + sysvals.vprint('Analyzing the ftrace data (%s)...' % \ + os.path.basename(sysvals.ftracefile)) if(os.path.exists(sysvals.ftracefile) == False): doError('%s does not exist' % sysvals.ftracefile) - - sysvals.setupAllKprobes() + if not live: + sysvals.setupAllKprobes() tracewatch = [] if sysvals.usekprobes: tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend', @@ -2458,7 +2681,7 @@ def parseTraceLog(): testdata = [] testrun = 0 data = 0 - tf = open(sysvals.ftracefile, 'r') + tf = sysvals.openlog(sysvals.ftracefile, 'r') phase = 'suspend_prepare' for line in tf: # remove any latent carriage returns @@ -2470,6 +2693,9 @@ def parseTraceLog(): elif re.match(tp.sysinfofmt, line): tp.sysinfo = line continue + elif re.match(tp.cmdlinefmt, line): + tp.cmdline = line + continue # firmware line: pull out any firmware data m = re.match(sysvals.firmwarefmt, line) if(m): @@ -2591,6 +2817,8 @@ def parseTraceLog(): phase = 'suspend_prepare' if(not isbegin): data.dmesg[phase]['end'] = t.time + if data.dmesg[phase]['start'] < 0: + data.dmesg[phase]['start'] = data.start continue # suspend start elif(re.match('dpm_suspend\[.*', t.name)): @@ -2604,6 +2832,8 @@ def parseTraceLog(): continue # suspend_noirq start elif(re.match('dpm_suspend_noirq\[.*', t.name)): + if data.phaseCollision('suspend_noirq', isbegin, line): + continue phase = 'suspend_noirq' data.setPhase(phase, t.time, isbegin) if(not isbegin): @@ -2636,6 +2866,8 @@ def parseTraceLog(): continue # resume_noirq start elif(re.match('dpm_resume_noirq\[.*', t.name)): + if data.phaseCollision('resume_noirq', isbegin, line): + continue phase = 'resume_noirq' data.setPhase(phase, t.time, isbegin) if(isbegin): @@ -2742,11 +2974,14 @@ def parseTraceLog(): key = (m_proc, pid) if(key not in testrun.ftemp): testrun.ftemp[key] = [] - testrun.ftemp[key].append(FTraceCallGraph(pid)) + testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals)) # when the call is finished, see which device matches it cg = testrun.ftemp[key][-1] - if(cg.addLine(t)): - testrun.ftemp[key].append(FTraceCallGraph(pid)) + res = cg.addLine(t) + if(res != 0): + testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals)) + if(res == -1): + testrun.ftemp[key][-1].addLine(t) tf.close() if sysvals.suspendmode == 'command': @@ -2812,28 +3047,31 @@ def parseTraceLog(): for key in test.ftemp: proc, pid = key for cg in test.ftemp[key]: - if len(cg.list) < 1 or cg.invalid: + if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0): continue if(not cg.postProcess()): id = 'task %s' % (pid) - vprint('Sanity check failed for '+\ + sysvals.vprint('Sanity check failed for '+\ id+', ignoring this callback') continue # match cg data to devices - if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, data): + devname = '' + if sysvals.suspendmode != 'command': + devname = cg.deviceMatch(pid, data) + if not devname: sortkey = '%f%f%d' % (cg.start, cg.end, pid) sortlist[sortkey] = cg + elif len(cg.list) > 1000000: + print 'WARNING: the callgraph for %s is massive (%d lines)' %\ + (devname, len(cg.list)) # create blocks for orphan cg data for sortkey in sorted(sortlist): cg = sortlist[sortkey] name = cg.name if sysvals.isCallgraphFunc(name): - vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) + sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) cg.newActionFromFunction(data) - if sysvals.suspendmode == 'command': - for data in testdata: - data.printDetails() return testdata # fill in any missing phases @@ -2841,7 +3079,7 @@ def parseTraceLog(): lp = data.phases[0] for p in data.phases: if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0): - vprint('WARNING: phase "%s" is missing!' % p) + sysvals.vprint('WARNING: phase "%s" is missing!' % p) if(data.dmesg[p]['start'] < 0): data.dmesg[p]['start'] = data.dmesg[lp]['end'] if(p == 'resume_machine'): @@ -2859,7 +3097,6 @@ def parseTraceLog(): data.fixupInitcallsThatDidntReturn() if sysvals.usedevsrc: data.optimizeDevSrc() - data.printDetails() # x2: merge any overlapping devices between test runs if sysvals.usedevsrc and len(testdata) > 1: @@ -2878,19 +3115,18 @@ def parseTraceLog(): # The dmesg filename is taken from sysvals # Output: # An array of empty Data objects with only their dmesgtext attributes set -def loadKernelLog(justtext=False): - vprint('Analyzing the dmesg data...') +def loadKernelLog(): + sysvals.vprint('Analyzing the dmesg data (%s)...' % \ + os.path.basename(sysvals.dmesgfile)) if(os.path.exists(sysvals.dmesgfile) == False): doError('%s does not exist' % sysvals.dmesgfile) - if justtext: - dmesgtext = [] # there can be multiple test runs in a single file tp = TestProps() tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown') testruns = [] data = 0 - lf = open(sysvals.dmesgfile, 'r') + lf = sysvals.openlog(sysvals.dmesgfile, 'r') for line in lf: line = line.replace('\r\n', '') idx = line.find('[') @@ -2903,6 +3139,9 @@ def loadKernelLog(justtext=False): elif re.match(tp.sysinfofmt, line): tp.sysinfo = line continue + elif re.match(tp.cmdlinefmt, line): + tp.cmdline = line + continue m = re.match(sysvals.firmwarefmt, line) if(m): tp.fwdata.append((int(m.group('s')), int(m.group('r')))) @@ -2911,9 +3150,6 @@ def loadKernelLog(justtext=False): if(not m): continue msg = m.group("msg") - if justtext: - dmesgtext.append(line) - continue if(re.match('PM: Syncing filesystems.*', msg)): if(data): testruns.append(data) @@ -2934,8 +3170,6 @@ def loadKernelLog(justtext=False): data.dmesgtext.append(line) lf.close() - if justtext: - return dmesgtext if data: testruns.append(data) if len(testruns) < 1: @@ -2975,7 +3209,7 @@ def parseKernelLog(data): phase = 'suspend_runtime' if(data.fwValid): - vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \ + sysvals.vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \ (data.fwSuspend, data.fwResume)) # dmesg phase match table @@ -3201,7 +3435,6 @@ def parseKernelLog(data): for event in actions[name]: data.newActionGlobal(name, event['begin'], event['end']) - data.printDetails() if(len(sysvals.devicefilter) > 0): data.deviceFilter(sysvals.devicefilter) data.fixupInitcallsThatDidntReturn() @@ -3230,9 +3463,9 @@ def callgraphHTML(sv, hf, num, cg, title, color, devid): else: fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>' flen = fmt % (line.length*1000, line.time) - if(line.freturn and line.fcall): + if line.isLeaf(): hf.write(html_func_leaf.format(line.name, flen)) - elif(line.freturn): + elif line.freturn: hf.write(html_func_end) else: hf.write(html_func_start.format(num, line.name, flen)) @@ -3249,7 +3482,7 @@ def addCallgraphs(sv, hf, data): continue list = data.dmesg[p]['list'] for devname in data.sortedDevices(p): - if len(sv.devicefilter) > 0 and devname not in sv.devicefilter: + if len(sv.cgfilter) > 0 and devname not in sv.cgfilter: continue dev = list[devname] color = 'white' @@ -3270,7 +3503,6 @@ def addCallgraphs(sv, hf, data): for cg in dev['ftraces']: num = callgraphHTML(sv, hf, num, cg, name+' → '+cg.name, color, dev['id']) - hf.write('\n\n </section>\n') # Function: createHTMLSummarySimple @@ -3311,7 +3543,7 @@ def createHTMLSummarySimple(testruns, htmlfile, folder): sTimeAvg = rTimeAvg = 0.0 mode = '' num = 0 - for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'])): + for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): if mode != data['mode']: # test average line if(num > 0): @@ -3387,7 +3619,7 @@ def createHTML(testruns): data.normalizeTime(testruns[-1].tSuspended) # html function templates - html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n' + html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}→</div>\n' html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' html_timetotal = '<table class="time1">\n<tr>'\ @@ -3416,20 +3648,17 @@ def createHTML(testruns): scaleH = 40 # device timeline - vprint('Creating Device Timeline...') - devtl = Timeline(30, scaleH) # write the test title and general info header - devtl.createHeader(sysvals) + devtl.createHeader(sysvals, testruns[0].stamp) # Generate the header for this timeline for data in testruns: tTotal = data.end - data.start sktime, rktime = data.getTimeValues() if(tTotal == 0): - print('ERROR: No timeline data') - sys.exit() + doError('No timeline data') if(data.tLow > 0): low_time = '%.0f'%(data.tLow*1000) if sysvals.suspendmode == 'command': @@ -3567,9 +3796,10 @@ def createHTML(testruns): data.dmesg[b]['color'], '') for e in data.errorinfo[dir]: # draw red lines for any kernel errors found - t, err = e + type, t, idx1, idx2 = e + id = '%d_%d' % (idx1, idx2) right = '%f' % (((mMax-t)*100.0)/mTotal) - devtl.html += html_error.format(right, err) + devtl.html += html_error.format(right, id, type) for b in sorted(phases[dir]): # draw the devices for this phase phaselist = data.dmesg[b]['list'] @@ -3663,14 +3893,7 @@ def createHTML(testruns): devtl.html += '</div>\n' hf = open(sysvals.htmlfile, 'w') - - # no header or css if its embedded - if(sysvals.embedded): - hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' % - (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \ - data.fwSuspend/1000000, data.fwResume/1000000)) - else: - addCSS(hf, sysvals, len(testruns), kerror) + addCSS(hf, sysvals, len(testruns), kerror) # write the device timeline hf.write(devtl.html) @@ -3701,7 +3924,7 @@ def createHTML(testruns): data = testruns[sysvals.cgtest] else: data = testruns[-1] - if(sysvals.usecallgraph and not sysvals.embedded): + if sysvals.usecallgraph: addCallgraphs(sysvals, hf, data) # add the test log as a hidden div @@ -3710,7 +3933,7 @@ def createHTML(testruns): # add the dmesg log as a hidden div if sysvals.dmesglog and sysvals.dmesgfile: hf.write('<div id="dmesglog" style="display:none;">\n') - lf = open(sysvals.dmesgfile, 'r') + lf = sysvals.openlog(sysvals.dmesgfile, 'r') for line in lf: line = line.replace('<', '<').replace('>', '>') hf.write(line) @@ -3719,28 +3942,15 @@ def createHTML(testruns): # add the ftrace log as a hidden div if sysvals.ftracelog and sysvals.ftracefile: hf.write('<div id="ftracelog" style="display:none;">\n') - lf = open(sysvals.ftracefile, 'r') + lf = sysvals.openlog(sysvals.ftracefile, 'r') for line in lf: hf.write(line) lf.close() hf.write('</div>\n') - if(not sysvals.embedded): - # write the footer and close - addScriptCode(hf, testruns) - hf.write('</body>\n</html>\n') - else: - # embedded out will be loaded in a page, skip the js - t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000 - tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000 - # add js code in a div entry for later evaluation - detail = 'var bounds = [%f,%f];\n' % (t0, tMax) - detail += 'var devtable = [\n' - for data in testruns: - topo = data.deviceTopology() - detail += '\t"%s",\n' % (topo) - detail += '];\n' - hf.write('<div id=customcode style=display:none>\n'+detail+'</div>\n') + # write the footer and close + addScriptCode(hf, testruns) + hf.write('</body>\n</html>\n') hf.close() return True @@ -4149,9 +4359,25 @@ def addScriptCode(hf, testruns): ' win.document.write(html+dt);\n'\ ' }\n'\ ' function errWindow() {\n'\ - ' var text = this.id;\n'\ + ' var range = this.id.split("_");\n'\ + ' var idx1 = parseInt(range[0]);\n'\ + ' var idx2 = parseInt(range[1]);\n'\ ' var win = window.open();\n'\ - ' win.document.write("<pre>"+text+"</pre>");\n'\ + ' var log = document.getElementById("dmesglog");\n'\ + ' var title = "<title>dmesg log</title>";\n'\ + ' var text = log.innerHTML.split("\\n");\n'\ + ' var html = "";\n'\ + ' for(var i = 0; i < text.length; i++) {\n'\ + ' if(i == idx1) {\n'\ + ' html += "<e id=target>"+text[i]+"</e>\\n";\n'\ + ' } else if(i > idx1 && i <= idx2) {\n'\ + ' html += "<e>"+text[i]+"</e>\\n";\n'\ + ' } else {\n'\ + ' html += text[i]+"\\n";\n'\ + ' }\n'\ + ' }\n'\ + ' win.document.write("<style>e{color:red}</style>"+title+"<pre>"+html+"</pre>");\n'\ + ' win.location.hash = "#target";\n'\ ' win.document.close();\n'\ ' }\n'\ ' function logWindow(e) {\n'\ @@ -4219,6 +4445,30 @@ def addScriptCode(hf, testruns): '</script>\n' hf.write(script_code); +def setRuntimeSuspend(before=True): + global sysvals + sv = sysvals + if sv.rs == 0: + return + if before: + # runtime suspend disable or enable + if sv.rs > 0: + sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled' + else: + sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled' + print('CONFIGURING RUNTIME SUSPEND...') + sv.rslist = deviceInfo(sv.rstgt) + for i in sv.rslist: + sv.setVal(sv.rsval, i) + print('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist))) + print('waiting 5 seconds...') + time.sleep(5) + else: + # runtime suspend re-enable or re-disable + for i in sv.rslist: + sv.setVal(sv.rstgt, i) + print('runtime suspend settings restored on %d devices' % len(sv.rslist)) + # Function: executeSuspend # Description: # Execute system suspend through the sysfs interface, then copy the output @@ -4227,6 +4477,19 @@ def executeSuspend(): pm = ProcessMonitor() tp = sysvals.tpath fwdata = [] + # run these commands to prepare the system for suspend + if sysvals.display: + if sysvals.display > 0: + print('TURN DISPLAY ON') + call('xset -d :0.0 dpms force suspend', shell=True) + call('xset -d :0.0 dpms force on', shell=True) + else: + print('TURN DISPLAY OFF') + call('xset -d :0.0 dpms force suspend', shell=True) + time.sleep(1) + if sysvals.sync: + print('SYNCING FILESYSTEMS') + call('sync', shell=True) # mark the start point in the kernel ring buffer just as we start sysvals.initdmesg() # start ftrace @@ -4298,47 +4561,22 @@ def executeSuspend(): pm.stop() sysvals.fsetVal('0', 'tracing_on') print('CAPTURING TRACE') - sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata) - call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True) + op = sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata) + fp = open(tp+'trace', 'r') + for line in fp: + op.write(line) + op.close() sysvals.fsetVal('', 'trace') devProps() # grab a copy of the dmesg output print('CAPTURING DMESG') - sysvals.writeDatafileHeader(sysvals.dmesgfile, fwdata) - sysvals.getdmesg() + sysvals.getdmesg(fwdata) -# Function: setUSBDevicesAuto -# Description: -# Set the autosuspend control parameter of all USB devices to auto -# This can be dangerous, so use at your own risk, most devices are set -# to always-on since the kernel cant determine if the device can -# properly autosuspend -def setUSBDevicesAuto(): - sysvals.rootCheck(True) - for dirname, dirnames, filenames in os.walk('/sys/devices'): - if(re.match('.*/usb[0-9]*.*', dirname) and - 'idVendor' in filenames and 'idProduct' in filenames): - call('echo auto > %s/power/control' % dirname, shell=True) - name = dirname.split('/')[-1] - desc = Popen(['cat', '%s/product' % dirname], - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') - ctrl = Popen(['cat', '%s/power/control' % dirname], - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') - print('control is %s for %6s: %s' % (ctrl, name, desc)) - -# Function: yesno -# Description: -# Print out an equivalent Y or N for a set of known parameter values -# Output: -# 'Y', 'N', or ' ' if the value is unknown -def yesno(val): - yesvals = ['auto', 'enabled', 'active', '1'] - novals = ['on', 'disabled', 'suspended', 'forbidden', 'unsupported'] - if val in yesvals: - return 'Y' - elif val in novals: - return 'N' - return ' ' +def readFile(file): + if os.path.islink(file): + return os.readlink(file).split('/')[-1] + else: + return sysvals.getVal(file).strip() # Function: ms2nice # Description: @@ -4346,69 +4584,81 @@ def yesno(val): # Output: # The time string, e.g. "1901m16s" def ms2nice(val): - ms = 0 - try: - ms = int(val) - except: - return 0.0 - m = ms / 60000 - s = (ms / 1000) - (m * 60) - return '%3dm%2ds' % (m, s) + val = int(val) + h = val / 3600000 + m = (val / 60000) % 60 + s = (val / 1000) % 60 + if h > 0: + return '%d:%02d:%02d' % (h, m, s) + if m > 0: + return '%02d:%02d' % (m, s) + return '%ds' % s -# Function: detectUSB +def yesno(val): + list = {'enabled':'A', 'disabled':'S', 'auto':'E', 'on':'D', + 'active':'A', 'suspended':'S', 'suspending':'S'} + if val not in list: + return ' ' + return list[val] + +# Function: deviceInfo # Description: # Detect all the USB hosts and devices currently connected and add # a list of USB device names to sysvals for better timeline readability -def detectUSB(): - field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''} - power = {'async':'', 'autosuspend':'', 'autosuspend_delay_ms':'', - 'control':'', 'persist':'', 'runtime_enabled':'', - 'runtime_status':'', 'runtime_usage':'', - 'runtime_active_time':'', - 'runtime_suspended_time':'', - 'active_duration':'', - 'connected_duration':''} - - print('LEGEND') - print('---------------------------------------------------------------------------------------------') - print(' A = async/sync PM queue Y/N D = autosuspend delay (seconds)') - print(' S = autosuspend Y/N rACTIVE = runtime active (min/sec)') - print(' P = persist across suspend Y/N rSUSPEN = runtime suspend (min/sec)') - print(' E = runtime suspend enabled/forbidden Y/N ACTIVE = active duration (min/sec)') - print(' R = runtime status active/suspended Y/N CONNECT = connected duration (min/sec)') - print(' U = runtime usage count') - print('---------------------------------------------------------------------------------------------') - print(' NAME ID DESCRIPTION SPEED A S P E R U D rACTIVE rSUSPEN ACTIVE CONNECT') - print('---------------------------------------------------------------------------------------------') - +def deviceInfo(output=''): + if not output: + print('LEGEND') + print('---------------------------------------------------------------------------------------------') + print(' A = async/sync PM queue (A/S) C = runtime active children') + print(' R = runtime suspend enabled/disabled (E/D) rACTIVE = runtime active (min/sec)') + print(' S = runtime status active/suspended (A/S) rSUSPEND = runtime suspend (min/sec)') + print(' U = runtime usage count') + print('---------------------------------------------------------------------------------------------') + print('DEVICE NAME A R S U C rACTIVE rSUSPEND') + print('---------------------------------------------------------------------------------------------') + + res = [] + tgtval = 'runtime_status' + lines = dict() for dirname, dirnames, filenames in os.walk('/sys/devices'): - if(re.match('.*/usb[0-9]*.*', dirname) and - 'idVendor' in filenames and 'idProduct' in filenames): - for i in field: - field[i] = Popen(['cat', '%s/%s' % (dirname, i)], - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') - name = dirname.split('/')[-1] - for i in power: - power[i] = Popen(['cat', '%s/power/%s' % (dirname, i)], - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') - if(re.match('usb[0-9]*', name)): - first = '%-8s' % name - else: - first = '%8s' % name - print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \ - (first, field['idVendor'], field['idProduct'], \ - field['product'][0:20], field['speed'], \ - yesno(power['async']), \ - yesno(power['control']), \ - yesno(power['persist']), \ - yesno(power['runtime_enabled']), \ - yesno(power['runtime_status']), \ - power['runtime_usage'], \ - power['autosuspend'], \ - ms2nice(power['runtime_active_time']), \ - ms2nice(power['runtime_suspended_time']), \ - ms2nice(power['active_duration']), \ - ms2nice(power['connected_duration']))) + if(not re.match('.*/power', dirname) or + 'control' not in filenames or + tgtval not in filenames): + continue + name = '' + dirname = dirname[:-6] + device = dirname.split('/')[-1] + power = dict() + power[tgtval] = readFile('%s/power/%s' % (dirname, tgtval)) + # only list devices which support runtime suspend + if power[tgtval] not in ['active', 'suspended', 'suspending']: + continue + for i in ['product', 'driver', 'subsystem']: + file = '%s/%s' % (dirname, i) + if os.path.exists(file): + name = readFile(file) + break + for i in ['async', 'control', 'runtime_status', 'runtime_usage', + 'runtime_active_kids', 'runtime_active_time', + 'runtime_suspended_time']: + if i in filenames: + power[i] = readFile('%s/power/%s' % (dirname, i)) + if output: + if power['control'] == output: + res.append('%s/power/control' % dirname) + continue + lines[dirname] = '%-26s %-26s %1s %1s %1s %1s %1s %10s %10s' % \ + (device[:26], name[:26], + yesno(power['async']), \ + yesno(power['control']), \ + yesno(power['runtime_status']), \ + power['runtime_usage'], \ + power['runtime_active_kids'], \ + ms2nice(power['runtime_active_time']), \ + ms2nice(power['runtime_suspended_time'])) + for i in sorted(lines): + print lines[i] + return res # Function: devProps # Description: @@ -4444,7 +4694,7 @@ def devProps(data=0): msghead = 'Additional data added by AnalyzeSuspend' alreadystamped = False tp = TestProps() - tf = open(sysvals.ftracefile, 'r') + tf = sysvals.openlog(sysvals.ftracefile, 'r') for line in tf: if msghead in line: alreadystamped = True @@ -4469,7 +4719,7 @@ def devProps(data=0): if not alreadystamped and sysvals.suspendmode == 'command': out = '#\n# '+msghead+'\n# Device Properties: ' out += 'testcommandstring,%s,0;' % (sysvals.testcommand) - with open(sysvals.ftracefile, 'a') as fp: + with sysvals.openlog(sysvals.ftracefile, 'a') as fp: fp.write(out+'\n') sysvals.devprops = props return @@ -4526,7 +4776,7 @@ def devProps(data=0): out = '#\n# '+msghead+'\n# Device Properties: ' for dev in sorted(props): out += props[dev].out(dev) - with open(sysvals.ftracefile, 'a') as fp: + with sysvals.openlog(sysvals.ftracefile, 'a') as fp: fp.write(out+'\n') sysvals.devprops = props @@ -4869,20 +5119,12 @@ def statusCheck(probecheck=False): # what data source are we using res = 'DMESG' if(ftgood): - sysvals.usetraceeventsonly = True - sysvals.usetraceevents = False + sysvals.usetraceevents = True for e in sysvals.traceevents: - check = False - if(os.path.exists(sysvals.epath+e)): - check = True - if(not check): - sysvals.usetraceeventsonly = False - if(e == 'suspend_resume' and check): - sysvals.usetraceevents = True - if(sysvals.usetraceevents and sysvals.usetraceeventsonly): + if not os.path.exists(sysvals.epath+e): + sysvals.usetraceevents = False + if(sysvals.usetraceevents): res = 'FTRACE (all trace events found)' - elif(sysvals.usetraceevents): - res = 'DMESG and FTRACE (suspend_resume trace event found)' print(' timeline data source: %s' % res) # check if rtcwake @@ -4917,6 +5159,7 @@ def doError(msg, help=False): if(help == True): printHelp() print('ERROR: %s\n') % msg + sysvals.outputResult({'error':msg}) sys.exit() # Function: getArgInt @@ -4957,22 +5200,36 @@ def getArgFloat(name, args, min, max, main=True): doError(name+': value should be between %f and %f' % (min, max), True) return val -def processData(): +def processData(live=False): print('PROCESSING DATA') - if(sysvals.usetraceeventsonly): - testruns = parseTraceLog() + if(sysvals.usetraceevents): + testruns = parseTraceLog(live) if sysvals.dmesgfile: - dmesgtext = loadKernelLog(True) for data in testruns: - data.extractErrorInfo(dmesgtext) + data.extractErrorInfo() else: testruns = loadKernelLog() for data in testruns: parseKernelLog(data) if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): appendIncompleteTraceLog(testruns) + sysvals.vprint('Command:\n %s' % sysvals.cmdline) + for data in testruns: + data.printDetails() + if sysvals.cgdump: + for data in testruns: + data.debugPrint() + sys.exit() + + sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) createHTML(testruns) - return testruns + print('DONE') + data = testruns[0] + stamp = data.stamp + stamp['suspend'], stamp['resume'] = data.getTimeValues() + if data.fwValid: + stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume + return (testruns, stamp) # Function: rerunTest # Description: @@ -4980,37 +5237,36 @@ def processData(): def rerunTest(): if sysvals.ftracefile: doesTraceLogHaveTraceEvents() - if not sysvals.dmesgfile and not sysvals.usetraceeventsonly: + if not sysvals.dmesgfile and not sysvals.usetraceevents: doError('recreating this html output requires a dmesg file') sysvals.setOutputFile() - vprint('Output file: %s' % sysvals.htmlfile) if os.path.exists(sysvals.htmlfile): if not os.path.isfile(sysvals.htmlfile): doError('a directory already exists with this name: %s' % sysvals.htmlfile) elif not os.access(sysvals.htmlfile, os.W_OK): doError('missing permission to write to %s' % sysvals.htmlfile) - return processData() + testruns, stamp = processData(False) + return stamp # Function: runTest # Description: # execute a suspend/resume, gather the logs, and generate the output -def runTest(): +def runTest(n=0): # prepare for the test sysvals.initFtrace() sysvals.initTestOutput('suspend') - vprint('Output files:\n\t%s\n\t%s\n\t%s' % \ - (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile)) # execute the test executeSuspend() sysvals.cleanupFtrace() - processData() - - # if running as root, change output dir owner to sudo_user - if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \ - 'SUDO_USER' in os.environ: - cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' - call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) + if sysvals.skiphtml: + sysvals.sudouser(sysvals.testdir) + return + testruns, stamp = processData(True) + for data in testruns: + del data + sysvals.sudouser(sysvals.testdir) + sysvals.outputResult(stamp, n) def find_in_html(html, strs, div=False): for str in strs: @@ -5072,10 +5328,12 @@ def runSummary(subdir, local=True): # Function: checkArgBool # Description: # check if a boolean string value is true or false -def checkArgBool(value): - yes = ['1', 'true', 'yes', 'on'] - if value.lower() in yes: +def checkArgBool(name, value): + if value in switchvalues: + if value in switchoff: + return False return True + doError('invalid boolean --> (%s: %s), use "true/false" or "1/0"' % (name, value), True) return False # Function: configFromFile @@ -5091,60 +5349,116 @@ def configFromFile(file): if 'Settings' in sections: for opt in Config.options('Settings'): value = Config.get('Settings', opt).lower() - if(opt.lower() == 'verbose'): - sysvals.verbose = checkArgBool(value) - elif(opt.lower() == 'addlogs'): - sysvals.dmesglog = sysvals.ftracelog = checkArgBool(value) - elif(opt.lower() == 'dev'): - sysvals.usedevsrc = checkArgBool(value) - elif(opt.lower() == 'proc'): - sysvals.useprocmon = checkArgBool(value) - elif(opt.lower() == 'x2'): - if checkArgBool(value): + option = opt.lower() + if(option == 'verbose'): + sysvals.verbose = checkArgBool(option, value) + elif(option == 'addlogs'): + sysvals.dmesglog = sysvals.ftracelog = checkArgBool(option, value) + elif(option == 'dev'): + sysvals.usedevsrc = checkArgBool(option, value) + elif(option == 'proc'): + sysvals.useprocmon = checkArgBool(option, value) + elif(option == 'x2'): + if checkArgBool(option, value): sysvals.execcount = 2 - elif(opt.lower() == 'callgraph'): - sysvals.usecallgraph = checkArgBool(value) - elif(opt.lower() == 'override-timeline-functions'): - overridekprobes = checkArgBool(value) - elif(opt.lower() == 'override-dev-timeline-functions'): - overridedevkprobes = checkArgBool(value) - elif(opt.lower() == 'devicefilter'): + elif(option == 'callgraph'): + sysvals.usecallgraph = checkArgBool(option, value) + elif(option == 'override-timeline-functions'): + overridekprobes = checkArgBool(option, value) + elif(option == 'override-dev-timeline-functions'): + overridedevkprobes = checkArgBool(option, value) + elif(option == 'skiphtml'): + sysvals.skiphtml = checkArgBool(option, value) + elif(option == 'sync'): + sysvals.sync = checkArgBool(option, value) + elif(option == 'rs' or option == 'runtimesuspend'): + if value in switchvalues: + if value in switchoff: + sysvals.rs = -1 + else: + sysvals.rs = 1 + else: + doError('invalid value --> (%s: %s), use "enable/disable"' % (option, value), True) + elif(option == 'display'): + if value in switchvalues: + if value in switchoff: + sysvals.display = -1 + else: + sysvals.display = 1 + else: + doError('invalid value --> (%s: %s), use "on/off"' % (option, value), True) + elif(option == 'gzip'): + sysvals.gzip = checkArgBool(option, value) + elif(option == 'cgfilter'): + sysvals.setCallgraphFilter(value) + elif(option == 'cgskip'): + if value in switchoff: + sysvals.cgskip = '' + else: + sysvals.cgskip = sysvals.configFile(val) + if(not sysvals.cgskip): + doError('%s does not exist' % sysvals.cgskip) + elif(option == 'cgtest'): + sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False) + elif(option == 'cgphase'): + d = Data(0) + if value not in d.phases: + doError('invalid phase --> (%s: %s), valid phases are %s'\ + % (option, value, d.phases), True) + sysvals.cgphase = value + elif(option == 'fadd'): + file = sysvals.configFile(value) + if(not file): + doError('%s does not exist' % value) + sysvals.addFtraceFilterFunctions(file) + elif(option == 'result'): + sysvals.result = value + elif(option == 'multi'): + nums = value.split() + if len(nums) != 2: + doError('multi requires 2 integers (exec_count and delay)', True) + sysvals.multitest['run'] = True + sysvals.multitest['count'] = getArgInt('multi: n d (exec count)', nums[0], 2, 1000000, False) + sysvals.multitest['delay'] = getArgInt('multi: n d (delay between tests)', nums[1], 0, 3600, False) + elif(option == 'devicefilter'): sysvals.setDeviceFilter(value) - elif(opt.lower() == 'expandcg'): - sysvals.cgexp = checkArgBool(value) - elif(opt.lower() == 'srgap'): - if checkArgBool(value): + elif(option == 'expandcg'): + sysvals.cgexp = checkArgBool(option, value) + elif(option == 'srgap'): + if checkArgBool(option, value): sysvals.srgap = 5 - elif(opt.lower() == 'mode'): + elif(option == 'mode'): sysvals.suspendmode = value - elif(opt.lower() == 'command'): + elif(option == 'command' or option == 'cmd'): sysvals.testcommand = value - elif(opt.lower() == 'x2delay'): - sysvals.x2delay = getArgInt('-x2delay', value, 0, 60000, False) - elif(opt.lower() == 'predelay'): - sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False) - elif(opt.lower() == 'postdelay'): - sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False) - elif(opt.lower() == 'maxdepth'): - sysvals.max_graph_depth = getArgInt('-maxdepth', value, 0, 1000, False) - elif(opt.lower() == 'rtcwake'): - if value.lower() == 'off': + elif(option == 'x2delay'): + sysvals.x2delay = getArgInt('x2delay', value, 0, 60000, False) + elif(option == 'predelay'): + sysvals.predelay = getArgInt('predelay', value, 0, 60000, False) + elif(option == 'postdelay'): + sysvals.postdelay = getArgInt('postdelay', value, 0, 60000, False) + elif(option == 'maxdepth'): + sysvals.max_graph_depth = getArgInt('maxdepth', value, 0, 1000, False) + elif(option == 'rtcwake'): + if value in switchoff: sysvals.rtcwake = False else: sysvals.rtcwake = True - sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False) - elif(opt.lower() == 'timeprec'): - sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False)) - elif(opt.lower() == 'mindev'): - sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False) - elif(opt.lower() == 'callloop-maxgap'): - sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', value, 0.0, 1.0, False) - elif(opt.lower() == 'callloop-maxlen'): - sysvals.callloopmaxgap = getArgFloat('-callloop-maxlen', value, 0.0, 1.0, False) - elif(opt.lower() == 'mincg'): - sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False) - elif(opt.lower() == 'output-dir'): - sysvals.testdir = sysvals.setOutputFolder(value) + sysvals.rtcwaketime = getArgInt('rtcwake', value, 0, 3600, False) + elif(option == 'timeprec'): + sysvals.setPrecision(getArgInt('timeprec', value, 0, 6, False)) + elif(option == 'mindev'): + sysvals.mindevlen = getArgFloat('mindev', value, 0.0, 10000.0, False) + elif(option == 'callloop-maxgap'): + sysvals.callloopmaxgap = getArgFloat('callloop-maxgap', value, 0.0, 1.0, False) + elif(option == 'callloop-maxlen'): + sysvals.callloopmaxgap = getArgFloat('callloop-maxlen', value, 0.0, 1.0, False) + elif(option == 'mincg'): + sysvals.mincglen = getArgFloat('mincg', value, 0.0, 10000.0, False) + elif(option == 'bufsize'): + sysvals.bufsize = getArgInt('bufsize', value, 1, 1024*1024*8, False) + elif(option == 'output-dir'): + sysvals.outdir = sysvals.setOutputFolder(value) if sysvals.suspendmode == 'command' and not sysvals.testcommand: doError('No command supplied for mode "command"') @@ -5259,7 +5573,14 @@ def printHelp(): print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)') print(' -addlogs Add the dmesg and ftrace logs to the html output') print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') + print(' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)') + print(' -result fn Export a results table to a text file for parsing.') + print(' [testprep]') + print(' -sync Sync the filesystems before starting the test') + print(' -rs on/off Enable/disable runtime suspend for all devices, restore all after test') + print(' -display on/off Turn the display on or off for the test') print(' [advanced]') + print(' -gzip Gzip the trace and dmesg logs to save space') print(' -cmd {s} Run the timeline over a custom command, e.g. "sync -d"') print(' -proc Add usermode process info into the timeline (default: disabled)') print(' -dev Add kernel function calls and threads to the timeline (default: disabled)') @@ -5280,14 +5601,16 @@ def printHelp(): print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') + print(' -cgfilter S Filter the callgraph output in the timeline') + print(' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)') + print(' -bufsize N Set trace buffer size to N kilo-bytes (default: all of free memory)') print('') print('Other commands:') print(' -modes List available suspend modes') print(' -status Test to see if the system is enabled to run this tool') print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') print(' -sysinfo Print out system info extracted from BIOS') - print(' -usbtopo Print out the current USB topology with power info') - print(' -usbauto Enable autosuspend for all connected USB devices') + print(' -devinfo Print out the pm settings of all devices which support runtime suspend') print(' -flist Print the list of functions currently being captured in ftrace') print(' -flistall Print all functions capable of being captured in ftrace') print(' -summary directory Create a summary of all test in this dir') @@ -5301,9 +5624,9 @@ def printHelp(): # exec start (skipped if script is loaded as library) if __name__ == '__main__': cmd = '' - outdir = '' - multitest = {'run': False, 'count': 0, 'delay': 0} - simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status'] + simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status'] + if '-f' in sys.argv: + sysvals.cgskip = sysvals.configFile('cgskip.txt') # loop through the command line arguments args = iter(sys.argv[1:]) for arg in args: @@ -5333,6 +5656,10 @@ if __name__ == '__main__': sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000) elif(arg == '-f'): sysvals.usecallgraph = True + elif(arg == '-skiphtml'): + sysvals.skiphtml = True + elif(arg == '-cgdump'): + sysvals.cgdump = True elif(arg == '-addlogs'): sysvals.dmesglog = sysvals.ftracelog = True elif(arg == '-verbose'): @@ -5341,6 +5668,34 @@ if __name__ == '__main__': sysvals.useprocmon = True elif(arg == '-dev'): sysvals.usedevsrc = True + elif(arg == '-sync'): + sysvals.sync = True + elif(arg == '-gzip'): + sysvals.gzip = True + elif(arg == '-rs'): + try: + val = args.next() + except: + doError('-rs requires "enable" or "disable"', True) + if val.lower() in switchvalues: + if val.lower() in switchoff: + sysvals.rs = -1 + else: + sysvals.rs = 1 + else: + doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True) + elif(arg == '-display'): + try: + val = args.next() + except: + doError('-display requires "on" or "off"', True) + if val.lower() in switchvalues: + if val.lower() in switchoff: + sysvals.display = -1 + else: + sysvals.display = 1 + else: + doError('invalid option: %s, use "on/off"' % val, True) elif(arg == '-maxdepth'): sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000) elif(arg == '-rtcwake'): @@ -5348,7 +5703,7 @@ if __name__ == '__main__': val = args.next() except: doError('No rtcwake time supplied', True) - if val.lower() == 'off': + if val.lower() in switchoff: sysvals.rtcwake = False else: sysvals.rtcwake = True @@ -5359,6 +5714,8 @@ if __name__ == '__main__': sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0) elif(arg == '-mincg'): sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0) + elif(arg == '-bufsize'): + sysvals.bufsize = getArgInt('-bufsize', args, 1, 1024*1024*8) elif(arg == '-cgtest'): sysvals.cgtest = getArgInt('-cgtest', args, 0, 1) elif(arg == '-cgphase'): @@ -5368,8 +5725,26 @@ if __name__ == '__main__': doError('No phase name supplied', True) d = Data(0) if val not in d.phases: - doError('Invalid phase, valid phaess are %s' % d.phases, True) + doError('invalid phase --> (%s: %s), valid phases are %s'\ + % (arg, val, d.phases), True) sysvals.cgphase = val + elif(arg == '-cgfilter'): + try: + val = args.next() + except: + doError('No callgraph functions supplied', True) + sysvals.setCallgraphFilter(val) + elif(arg == '-cgskip'): + try: + val = args.next() + except: + doError('No file supplied', True) + if val.lower() in switchoff: + sysvals.cgskip = '' + else: + sysvals.cgskip = sysvals.configFile(val) + if(not sysvals.cgskip): + doError('%s does not exist' % sysvals.cgskip) elif(arg == '-callloop-maxgap'): sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0) elif(arg == '-callloop-maxlen'): @@ -5386,31 +5761,33 @@ if __name__ == '__main__': elif(arg == '-srgap'): sysvals.srgap = 5 elif(arg == '-multi'): - multitest['run'] = True - multitest['count'] = getArgInt('-multi n (exec count)', args, 2, 1000000) - multitest['delay'] = getArgInt('-multi d (delay between tests)', args, 0, 3600) + sysvals.multitest['run'] = True + sysvals.multitest['count'] = getArgInt('-multi n d (exec count)', args, 2, 1000000) + sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600) elif(arg == '-o'): try: val = args.next() except: doError('No subdirectory name supplied', True) - outdir = sysvals.setOutputFolder(val) + sysvals.outdir = sysvals.setOutputFolder(val) elif(arg == '-config'): try: val = args.next() except: doError('No text file supplied', True) - if(os.path.exists(val) == False): + file = sysvals.configFile(val) + if(not file): doError('%s does not exist' % val) - configFromFile(val) + configFromFile(file) elif(arg == '-fadd'): try: val = args.next() except: doError('No text file supplied', True) - if(os.path.exists(val) == False): + file = sysvals.configFile(val) + if(not file): doError('%s does not exist' % val) - sysvals.addFtraceFilterFunctions(val) + sysvals.addFtraceFilterFunctions(file) elif(arg == '-dmesg'): try: val = args.next() @@ -5435,7 +5812,7 @@ if __name__ == '__main__': except: doError('No directory supplied', True) cmd = 'summary' - outdir = val + sysvals.outdir = val sysvals.notestrun = True if(os.path.isdir(val) == False): doError('%s is not accesible' % val) @@ -5445,6 +5822,12 @@ if __name__ == '__main__': except: doError('No devnames supplied', True) sysvals.setDeviceFilter(val) + elif(arg == '-result'): + try: + val = args.next() + except: + doError('No result file supplied', True) + sysvals.result = val else: doError('Invalid argument: '+arg, True) @@ -5454,42 +5837,48 @@ if __name__ == '__main__': if(sysvals.usecallgraph and sysvals.useprocmon): doError('-proc is not compatible with -f') + if sysvals.usecallgraph and sysvals.cgskip: + sysvals.vprint('Using cgskip file: %s' % sysvals.cgskip) + sysvals.setCallgraphBlacklist(sysvals.cgskip) + # callgraph size cannot exceed device size if sysvals.mincglen < sysvals.mindevlen: sysvals.mincglen = sysvals.mindevlen - # just run a utility command and exit + # remove existing buffers before calculating memory + if(sysvals.usecallgraph or sysvals.usedevsrc): + sysvals.fsetVal('16', 'buffer_size_kb') sysvals.cpuInfo() + + # just run a utility command and exit if(cmd != ''): if(cmd == 'status'): statusCheck(True) elif(cmd == 'fpdt'): getFPDT(True) elif(cmd == 'sysinfo'): - sysvals.printSystemInfo() - elif(cmd == 'usbtopo'): - detectUSB() + sysvals.printSystemInfo(True) + elif(cmd == 'devinfo'): + deviceInfo() elif(cmd == 'modes'): print getModes() elif(cmd == 'flist'): sysvals.getFtraceFilterFunctions(True) elif(cmd == 'flistall'): sysvals.getFtraceFilterFunctions(False) - elif(cmd == 'usbauto'): - setUSBDevicesAuto() elif(cmd == 'summary'): - runSummary(outdir, True) + runSummary(sysvals.outdir, True) sys.exit() # if instructed, re-analyze existing data files if(sysvals.notestrun): - rerunTest() + stamp = rerunTest() + sysvals.outputResult(stamp) sys.exit() # verify that we can run a test if(not statusCheck()): - print('Check FAILED, aborting the test run!') - sys.exit() + doError('Check FAILED, aborting the test run!') # extract mem modes and convert mode = sysvals.suspendmode @@ -5509,25 +5898,35 @@ if __name__ == '__main__': sysvals.systemInfo(dmidecode(sysvals.mempath)) - if multitest['run']: + setRuntimeSuspend(True) + if sysvals.display: + call('xset -d :0.0 dpms 0 0 0', shell=True) + call('xset -d :0.0 s off', shell=True) + if sysvals.multitest['run']: # run multiple tests in a separate subdirectory - if not outdir: - s = 'suspend-x%d' % multitest['count'] - outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') - if not os.path.isdir(outdir): - os.mkdir(outdir) - for i in range(multitest['count']): + if not sysvals.outdir: + s = 'suspend-x%d' % sysvals.multitest['count'] + sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') + if not os.path.isdir(sysvals.outdir): + os.mkdir(sysvals.outdir) + for i in range(sysvals.multitest['count']): if(i != 0): - print('Waiting %d seconds...' % (multitest['delay'])) - time.sleep(multitest['delay']) - print('TEST (%d/%d) START' % (i+1, multitest['count'])) + print('Waiting %d seconds...' % (sysvals.multitest['delay'])) + time.sleep(sysvals.multitest['delay']) + print('TEST (%d/%d) START' % (i+1, sysvals.multitest['count'])) fmt = 'suspend-%y%m%d-%H%M%S' - sysvals.testdir = os.path.join(outdir, datetime.now().strftime(fmt)) - runTest() - print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count'])) - runSummary(outdir, False) + sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt)) + runTest(i+1) + print('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count'])) + sysvals.logmsg = '' + if not sysvals.skiphtml: + runSummary(sysvals.outdir, False) + sysvals.sudouser(sysvals.outdir) else: - if outdir: - sysvals.testdir = outdir + if sysvals.outdir: + sysvals.testdir = sysvals.outdir # run the test in the current directory runTest() + if sysvals.display: + call('xset -d :0.0 s reset', shell=True) + setRuntimeSuspend(False) |